iOS-底層原理(8)-block-本質(zhì),類型,copy屬性詳解

1.Block的本質(zhì)

  • block本質(zhì)上也是一個OC對象,它內(nèi)部也有個isa指針
  • block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象
  • block的底層結(jié)構(gòu)如下圖所示
image.png
image.png

代碼佐證

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

// block底層結(jié)構(gòu)體
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age;
};

// 1.block結(jié)構(gòu)體
void test1() {
    int age = 10;
    
    // 定義block
    void(^block)(int, int) = ^(int a, int b){
        NSLog(@"this is a block! -- %d", age);
        NSLog(@"a = %d, b = %d",a,b);
    };
    
    struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
    block(100,200);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 1. block結(jié)構(gòu)體
        test1();
    }
    return 0;
}
image.png
2.block的變量捕獲(capture)
  • 為了保證block內(nèi)部能夠正常訪問外部的變量,block有個變量捕獲機制
image.png
  • auto:自動變量,離開作用域就銷毀

代碼例子如下:

// 捕獲變量類型
void (^block)(void);

void blockTest() {
    int age = 10;
    static int height = 10;
    
    block = ^ {
        NSLog(@"age is %d, height is %d", age, height);
    };
    
    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        blockTest();
        block();
    }
    return 0;
}

運行結(jié)果

image.png
Block的本質(zhì) - NSObject對象
image.png
3.block的類型
  • block有3種類型,可以通過調(diào)用class方法或者isa指針查看具體類型,最終都是繼承自NSBlock類型
  • __NSGlobalBlock__ (_NSConcreteGlobalBlock )
  • __NSStackBlock__ ( _NSConcreteStackBlock )
  • __NSMallocBlock__ ( _NSConcreteMallocBlock )
image.png
image.png
  • 每一種類型的block調(diào)用copy后的結(jié)果如下所示
image.png

代碼例子如下

  • 先將環(huán)境切換為MRC,setting -> OC Automitic RF -> NO
// 4.block的類型
int weight = 100;
void (^block)(void);

void blockClassType() {
    // 堆:動態(tài)分配內(nèi)存,需要程序員申請申請,也需要程序員自己管理內(nèi)存
    static int age = 10;
    // 局部static變量
    int height = 10;
    
    // Global:沒有訪問auto變量
    void(^block1)(void) = ^ {
        NSLog(@"block1");
    };
    // Globa2:訪問static變量
    void(^block2)(void) = ^ {
        NSLog(@"block2 - age = %d",age);
    };
    // Globa3:訪問全局變量
    void(^block3)(void) = ^ {
        NSLog(@"block3 - weight = %d",weight);
    };
    
    // Stack:訪問了auto變量
    void (^block4)(void) = ^{
        NSLog(@"block4 - height = %d", height);
    };
    
    // NSMallocBlock - 對StackBlock做copy操作
    block = [^{
        NSLog(@"block---------%d", height);
    } copy];
    [block release];
    
    NSLog(@"%@ %@ %@ %@ %@",
          [block1 class],
          [block2 class],
          [block3 class],
          [block4 class],
          [block class]
    );
}

運行結(jié)果如下

image.png
4. 數(shù)據(jù)存儲位置

代碼例子如下

// 5.數(shù)據(jù)存儲位置
int age = 100;
void dataLocationTest() {
    int a = 10;
    
    NSLog(@"數(shù)據(jù)段:age %p", &age);
    NSLog(@"棧:a %p", &a);
    NSLog(@"堆:obj %p", [[NSObject alloc] init]);
    NSLog(@"數(shù)據(jù)段:class %p", [Person class]);
}

打印結(jié)果

image.png
5.block的copy

在ARC環(huán)境下,編譯器會根據(jù)情況自動將棧上的block復(fù)制到堆上,比如以下情況

  • block作為函數(shù)返回值時
  • 將block賦值給__strong指針時
  • block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
  • block作為GCD API的方法參數(shù)時

代碼例子如下

  • 1.block作為函數(shù)返回值時
// 定義一個block
typedef void(^CSBlock)(void);

// 定義一個返回blcok的函數(shù)
CSBlock myBlock() {
    int age = 10;
    return ^{
        NSLog(@"age = %d",age);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 1.block作為函數(shù)返回值
        CSBlock block = myBlock();
        block();
        NSLog(@"%@",[block class]);
    }
    return 0;
}

運行結(jié)果

image.png
  • 2.將block賦值給__strong指針時
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 2.block有被強指針引用
        // ARC - 環(huán)境下 block - __NSMallocBlock__
        // MRC - 環(huán)境下 block - __NSStackBlock__
        int age = 10;
        CSBlock block = ^{
            NSLog(@"---------%d", age);
        };
        NSLog(@"%@", [block class]);
    }
    return 0;
}

打印結(jié)果

ARC.png
MRC.png
  • 3.block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
// 3.block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
NSArray *array = [NSArray array];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            
}];
  • 4.block作為GCD API的方法參數(shù)時
// 4.block作為GCD API的方法參數(shù)時
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{            
});
block建議寫法
  • MRC下block屬性的建議寫法
    @property (copy, nonatomic) void (^block)(void);

  • ARC下block屬性的建議寫法
    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);


本文參考借鑒MJ的教程視頻,非常感謝.


項目演示代碼如下
iOS_block_本質(zhì)
iOS-block-copy


更多block相關(guān)文章

iOS-copy底層原理之a(chǎn)uto變量

iOS-block底層原理詳解之__block屬性

iOS-block底層原理之循環(huán)引用詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,286評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,689評論 1 32
  • 面試題 block的原理是怎樣的?本質(zhì)是什么? __block的作用是什么?有什么使用注意點? block的屬性修...
    xx_cc閱讀 14,035評論 10 77
  • 抒情篇 坐在窗前無聊的刷朋友圈看別人的新年活動,突然收到你接受了他表白和他在一起了的消息,突然感覺好激動,但也覺得...
    octor閱讀 441評論 0 0
  • 美麗的花山公園,是淄博中心城區(qū)僅有的一處有山有水的公園,是淄博高新區(qū)政府歷經(jīng)2年之久、投資1.5億多進行了重...
    我愛執(zhí)著閱讀 404評論 0 0

友情鏈接更多精彩內(nèi)容