概述:
- 能夠截取自動變量的匿名函數
- 指向函數的指針
- 結構體
- oc對象
使用:
- 聲明
格式:返回值類型(^變量名稱)(參數列表)
int(^myBlock)(int a,int b)
void(^myBlock)(void)
- 定義(變量賦值)
格式:變量 = ^(參數列表){函數體};
myBlock = ^(int a, int b){
return 1;
};
myBlock = ^(void){
};
- 調用
格式:變量(參數列表);
myBlock(1,2);
myBlock();
- 使用typedef定義Block類型
格式:typedef 返回值類型(^變量名稱)(參數列表)
typedef void(^MyBlock)();
MyBlock myBlock = ^(){
};
- Block作為函數參數
int(^MyBlock)(int a, int b) = ^(int a, int b){ // 聲明定義一個參數為a,b,返回值為int類型的block
return 3;
};
[self blockTest:MyBlock];
void(^MyBlcok)(void) = ^(){
};
[self blockTest1:MyBlcok];
- (void)blockTest:(int(^)(int a, int b))myBlock{
}
- (void)blockTest1:(void(^)())myBlock{
}
注意:為了簡化block的聲明,一般使用typedef聲明block類型
- Block作為函數返回值
int(^MyBlock)(int a, int b) = [self blockTest];
- (int(^)(int a, int b))blockTest{
return ^(int a,int b){
return a+b;
};
}
block類型:
- 全局區:NSConcreteGlobalBlock
- 棧區:NSConcreteStackBlock
- 堆區:NSConcreteMallocBlock
- NSConcreteGlobalBlock
block定義在函數之外(和全局變量一個地方)
void (^globalBlock)() = ^{
NSLog(@"大家好我是NSConcreteGlobalBlock-->");
};
int main(int argc, char * argv[]) {
@autoreleasepool {
globalBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
轉換成c++代碼:
struct __globalBlock_block_impl_0 {
struct __block_impl impl;
struct __globalBlock_block_desc_0* Desc;
__globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock; <--- 看這,這里。。
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
注意:在block沒有截取任何自動變量的時候,也是NSConcreteGlobalBlock的,但我在ARC環境下,查看c++源碼,是NSConcreteStackBlock,具體原因請看唐巧大神的說明,自行驗證。
- NSConcreteStackBlock
定義在函數內部的block
int main(int argc, char * argv[]) {
@autoreleasepool {
void(^StackBlock)() = ^(){
NSLog(@"大家好,我是StackBlock");
};
StackBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
轉換成c++代碼:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
注意:NSConcreteMallocBlock無法直接創建,當執行了以下操作時,系統會自動從NSConcreteStackBlock copy到NSConcreteMallocBlock中:
1.調用Block的copy實例方法時
2.Block作為函數返回值返回時
3.將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時
4.將方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時
截取自動變量:
- 局部變量
- 局部變量在block中不可修改。
- 局部變量是值傳遞,所以在block外面修改不影響block里截取的變量值。
oc代碼:
int main(int argc, char * argv[]) {
@autoreleasepool {
int i = 0;
void(^MyBlock)() = ^(){
i++; // 報錯
NSLog(@"大家好,我是MyBlock-->%d",i); // 注釋掉上面的報錯代碼,輸出的還是0;
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代碼:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int i = __cself->i; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_658820_mi_1,i);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int i = 0;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- 靜態局部變量
- 局部靜態變量在block中可修改。
- 局部靜態變量是地址傳遞,所以在block外面修改會影響block里截取的變量值。
oc代碼:
int main(int argc, char * argv[]) {
@autoreleasepool {
static int i = 0;
void(^MyBlock)() = ^(){
i++; // 可以修改,不會報錯
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代碼:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *i;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_i, int flags=0) : i(_i) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *i = __cself->i; // bound by copy
(*i)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_e314e4_mi_0,(*i));
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
static int i = 0;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i));
i++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- __block修飾局部變量(只能用于修飾普通局部變量,static,全局都不行)
- __block修飾的局部變量在block中可修改。
- __block修飾的局部變量,注意看下面c++源碼,系統會把用__block修飾的變量包裝成一個__Block_byref_i_0結構體對象。main_block_impl_0 中引用的是 Block_byref_i_0 的結構體指針,這樣就可以達到修改外部變量的作用,因為傳遞的是該結構體的地址,所以我們在外面修改變量,會影響block中的值。
oc代碼:
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int i = 0;
void(^MyBlock)() = ^(){
i++; // 可以修改,不會報錯
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代碼:
struct __Block_byref_i_0 { // 當用__block來修飾變量時,系統會吧變量包裝成一個結構體對象。
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
(i->__forwarding->i)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_be9fd2_mi_0,(i->__forwarding->i));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
(i.__forwarding->i)++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- 全局變量
- 全局變量在block中可修改。
- 全局變量存儲在靜態數據區,在程序銷毀前不會銷毀,所以在block中可以直接訪問,因為訪問的是一份地址,所以在外面修改會影響block里面變量的值。
oc代碼:
int i = 0;
int main(int argc, char * argv[]) {
@autoreleasepool {
void(^MyBlock)() = ^(){
i++; // 可以修改,不會報錯
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代碼:
int i = 0;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
i++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_ba21a2_mi_0,i);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
i++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
- 靜態全局變量
- 靜態全局變量在block中可修改。
- 靜態全局變量存儲在靜態數據區,在程序銷毀前不會銷毀,所以在block中可以直接訪問,因為訪問的是一份地址,所以在外面修改會影響block里面變量的值。
oc代碼:
static int i = 0;
int main(int argc, char * argv[]) {
@autoreleasepool {
void(^MyBlock)() = ^(){
i++; // 可以修改,不會報錯
NSLog(@"大家好,我是MyBlock-->%d",i);
};
i++;
MyBlock();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
c++代碼:
static int i = 0;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
i++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_e20576_mi_0,i);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
i++;
((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
內存管理:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
int i = 0;
MyBlock = ^(){
NSLog(@"我是MyBlock--->%d",i);
};
MyBlock();
NSLog(@"myBlock--->%@",MyBlock);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
MyBlock();
}
- MRC
引用了局部變量i,所以block在NSStackBlock中,出了viewDidLoad方法,MyBlock會自動銷毀,touchesBegan再調用MyBlock就會報野指針錯誤。
需要手動調用Block_copy方法把block復制到NSConcreteMallocBlock中,記得調用Block_release方法使計數器-1。 - ARC
會自動調用copy方法,把block復制到NSConcreteMallocBlock中。
注意:在Block中無論是MRC/ARC,沒有調用局部變量時,都是NSConcreteGlobalBlock類型的,也就不會出現block銷毀了,在調用的情況了。
循環引用:
- 調用系統的block不會循環引用,self并不持有該方法。
[UIView animateWithDuration:0.5 animations:^{
NSLog(@"%@", self);
}];
- 當self持有block時,block中又調用了self,會循環引用。
@property (nonatomic, copy)MyBlock myBlock;
self.myBlock = ^(){
self.xxxx;
};
// 不會循環引用
__weak typeof(self)weakSelf = self;
self.myBlock = ^(){
weakSelf.xxxx;
};
- 自定義對象中有個block屬性,在調用該對象的block屬性中,調用該對象的其他屬性,會循環引用。
typedef void(^MyBlock)();
@interface MyBlockModel : NSObject
@property (nonatomic, copy) MyBlock myBlock;
@property (nonatomic, copy) NSString *text;
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
MyBlockModel *model = [[MyBlockModel alloc] init];
model.myBlock = ^(){
NSLog(@"--->%@",model.text);
};
// 不會循環引用
MyBlockModel *model1 = [[MyBlockModel alloc] init];
__weak MyBlockModel *weakModel = model1;
model1.myBlock = ^(){
NSLog(@"--->%@",weakModel.text);
};
}
- 注意:在異步(多線程)環境下。防止self被釋放,應該在block內部用強引用引用該弱引用。
__weak typeof(self)weakSelf = self;
self.myBlock = ^(){
__strong typeof (weakSelf)strongSelf = weakSelf;
if(strongSelf){
strongSelf.xxxx;
}
};
參考:
http://www.lxweimin.com/p/51d04b7639f1
http://www.cocoachina.com/ios/20161025/17198.html
http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
小提示:
1.如和把oc代碼轉成c++代碼:
通過終端cd到你要編譯的文件所在目錄,輸入:clang -rewrite-objc xxxx.m即可。(xxxx:你要編譯的文件名稱)。
2.如果出現main.m:9:9: fatal error: 'UIKit/UIKit.h' file not found怎么辦?
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode8.3.3.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxx.m(xxxx:你要編譯的文件名稱)。