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)文章