OC 代码块block
一、概述
Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block必须自己管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理 要么导致return cycle内存泄漏要么内存被提前释放导致crash。 Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,Block不仅 实现函数的功能,还能携带函数的执行环境。(摘自网络)。其灵活性体现在堆内存与栈内存的引用。并且,Block还可以作为函数的参数或返回值使用。
也可以这样理解,Block可以将其分成两部分:
- Block的执行代码,在编译时刻就已经生成。
- Block执行时需要一个包含所使用的和作用域范围内所有变量的值的数据结构。Block将所使用的变量和作用域范围内的变量的值制作成一个快照拷贝到栈上。当然,在使用Block_copy或者copy方法,会将这个数据结构拷贝到堆上,并且引用计数加1(仅针对指针,基本数据类型(比如:int)不在此范围之内,主要是原因是引用计数只能针对指针)。
注意:必须注意的是,在MRC模式下,NSStackBlock在函数返回值后,Block指针的内存会被回收,即Block变成野指针。容易犯的错误是
1 [muTableArray addObject:stackBlock];
在stackBlcok出栈后,stackBlcok函数返回值后,stackBlock会被回收,stackBlock变成野指针。当然,这是在MRC模式下,在ARC模式下,这是不需要担心的,因为在ARC模式下,Block默认会被拷贝到堆上。
二、Block格式
- 作为本地Block:
1 ReturnType (^blockName)(parameterType) = ^ReturnType (parameters...) 2 { 3 ... 4 };
- 作为Property:
1 @property (nonatomic, copy) ReturnType (^blockName)(parameterType);
- 作为方法参数:
1 - (void)someMethodAtTakesABlocks:(ReturnType (^)(parameterType))blockName;
- 作为一个方法实参被调用:
1 [someObject someMehtodThatTakesABlock:^ReturnType(parameters...){}];
- 声名为一个类型:
1 typedef ReturnType (^BlcokTypeName)(parameterType); 2 BlockTypeName blockName = ^ReturnType(parameterType){};
- 例子1:
1 - (void)somethings:(NSInteger (^)(NSString *aStr))blockName 2 { 3 blockName(@"somethings."); 4 } 5 6 - (void)senderSome 7 { 8 [self somethings:^NSInteger (NSString *aStr) 9 { 10 NSLog(@"print string aStr: %@", aStr); 11 return 1; 12 }]; 13 }
- 例子2:
1 typedef void (^MallocBlock)(NSString *typeStr); 2 MallocBlock stackBlk = ^void (NSString *typeStr){};
- 例子3:
1 void (^stackBlock)(NSString *typeStr) = ^(NSString *typeStr) 2 { 3 NSLog(@"%@", typeStr); 4 };
- 例子4:
1 @property (nonatomic, copy) void (^blkName)(void);
三、Block分类,Block分类在MRC或ARC模式下,分种是不同的。下面做详细介绍:
1. 在MRC下Block分类,分为NSGlobalBlock、NSStackBlock、NSMallocBlock。
- 1.0 NSGlobalBlock
- 1.1 NSGlobalBlock输出字符常量
-
1 void testGlobalBlock(void) 2 { 3 NSLog(@"Global block."); 4 5 void (^globalBlock)(void) = ^(void) 6 { 7 NSLog(@"global block"); 8 }; 9 10 globalBlock(); 11 12 NSLog(@"%@", globalBlock); 13 14 }
console log:
1 2015-08-11 10:27:31.846 LearnOC[1040:42569] Global block. 2 2015-08-11 10:27:31.847 LearnOC[1040:42569] global block 4 2015-08-11 10:27:31.848 LearnOC[1040:42569] <__NSGlobalBlock__: 0x100001040>
- 1.2 输出全局变量值
-
1 NSInteger var = 1; 2 void testGlobalBlock(void) 3 { 4 NSLog(@"Global block."); 5 NSLog(@"global block var address: %p", &var); 6 7 void (^globalBlock)(void) = ^(void) 8 { 9 NSLog(@"global block var address: %p", &var); 10 }; 11 12 globalBlock(); 13 14 NSLog(@"%@", globalBlock); 15 16 }
console log:
1 2015-08-11 10:27:31.846 LearnOC[1040:42569] Global block. 2 2015-08-11 10:27:31.847 LearnOC[1040:42569] global block var address: 0x100001148 3 2015-08-11 10:27:31.847 LearnOC[1040:42569] global block var address: 0x100001148 4 2015-08-11 10:27:31.848 LearnOC[1040:42569] <__NSGlobalBlock__: 0x100001040>
- 总结:在Block中,只要没有访问栈变量的Block都是NSGloablBlock。
-
- 2.0 NSStackBlock
- 2.1 NSStackBlock访问基本数据类型
1 void testStackBlock() 2 { 3 NSLog(@"Stack block"); 4 NSInteger var = 1; 5 6 void (^stackBlock)(void) = ^(void) 7 { 8 NSLog(@"stack block var: %ld", var); 9 }; 10 11 stackBlock(); 12 13 NSLog(@"stack func : %@", stackBlock); 14 }
console log:
1 2015-08-11 10:33:21.715 LearnOC[1065:44533] Stack block 2 2015-08-11 10:33:21.716 LearnOC[1065:44533] stack block var: 1 3 2015-08-11 10:33:21.716 LearnOC[1065:44533] stack func : <__NSStackBlock__: 0x7fff5fbff798>
总结:使用栈变量的Block皆是NSStackBlock。
- 2.2 使用__block修饰符,修饰变量对变量的影响
- 2.2.1 不使用__block修饰符
1 void testStackBlock() 2 { 3 NSLog(@"Stack block"); 4 NSInteger var = 1; 5 NSLog(@"stack var address: %p", &var); 6 7 void (^stackBlock)(void) = ^(void) 8 { 9 NSLog(@"stack block var address: %p", &var); 10 }; 11 12 stackBlock(); 13 14 NSLog(@"stack func : %@", stackBlock); 15 }
console log:
1 2015-08-11 10:45:02.639 LearnOC[1074:47943] Stack block 2 2015-08-11 10:45:02.641 LearnOC[1074:47943] stack var address: 0x7fff5fbff7c8 3 2015-08-11 10:45:02.641 LearnOC[1074:47943] stack block var address: 0x7fff5fbff7b8 4 2015-08-11 10:45:02.641 LearnOC[1074:47943] stack func : <__NSStackBlock__: 0x7fff5fbff798>
总结:在未使用__block修饰符时,Block体使用栈变量(即使用函数体内变量时)类似于函数参数的值传递。在Block体中使用的变量,是对外部分数据的拷贝,数据相同,地址不同。
- 2.2.2 __block修饰符
1 void testStackBlock() 2 { 3 NSLog(@"Stack block"); 4 __block NSInteger var = 1; 5 NSLog(@"stack var address: %p", &var); 6 7 void (^stackBlock)(void) = ^(void) 8 { 9 NSLog(@"stack block var address: %p", &var); 10 var = 2; 11 }; 12 13 stackBlock(); 14 15 NSLog(@"stack func : %@", stackBlock); 16 NSLog(@"stack var: %ld", var); 17 }
console log:
1 2015-08-11 10:58:15.385 LearnOC[1109:51436] Stack block 2 2015-08-11 10:58:15.387 LearnOC[1109:51436] stack var address: 0x7fff5fbff7c8 3 2015-08-11 10:58:15.387 LearnOC[1109:51436] stack block var address: 0x7fff5fbff7c8 4 2015-08-11 10:58:15.387 LearnOC[1109:51436] stack func : <__NSStackBlock__: 0x7fff5fbff770> 5 2015-08-11 10:58:15.387 LearnOC[1109:51436] stack var: 2
总结:使用__block修饰符,在Block中使用栈变量(即函数体内变量),变量是以C++中的引用(即别名)形式在Block体中使用栈变量。在Block体内外使用的栈变量的地址是相同的。注意:用__block修饰符修饰的变量在Block体内修改,整个数据就被改变了。
- 2.2.1 不使用__block修饰符
- 2.3 NSStackBlock 访问对象数据类型
- 2.3.1 未使用__block修饰符
1 #define MLog(comment, obj) NSLog(@"指针地址:%p, 对象地址:%p, 引用计数:%ld, 注释:%@", \ 2 &obj, obj, [obj retainCount], comment) 3 4 void testStackBlockPO() 5 { 6 NSObject *obj = [[NSObject alloc] init]; 7 MLog(@"Block", obj); 8 void (^stackBlock)(void) = ^(void) 9 { 10 MLog(@"Stack Block", obj); 11 12 }; 13 14 NSLog(@"Stack Block ptr: %@", stackBlock); 15 stackBlock(); 16 MLog(@"Block", obj); 17 }
console log:
1 2015-08-11 21:57:28.731 Myblock[8743:752759] 指针地址:0x7fff5fbff838, 对象地址:0x1001143a0, 引用计数:1, 注释:Block 2 2015-08-11 21:57:28.733 Myblock[8743:752759] Stack Block ptr: <__NSStackBlock__: 0x7fff5fbff808> 3 2015-08-11 21:57:28.733 Myblock[8743:752759] 指针地址:0x7fff5fbff828, 对象地址:0x1001143a0, 引用计数:1, 注释:Stack Block 4 2015-08-11 21:57:28.733 Myblock[8743:752759] 指针地址:0x7fff5fbff838, 对象地址:0x1001143a0, 引用计数:1, 注释:Block
- 2.3.2 使用__block修饰符
1 void testStackBlockPO() 2 { 3 __block NSObject *obj = [[NSObject alloc] init]; 4 MLog(@"Block", obj); 5 void (^stackBlock)(void) = ^(void) 6 { 7 MLog(@"Stack Block", obj); 8 9 }; 10 11 NSLog(@"Stack Block ptr: %@", stackBlock); 12 stackBlock(); 13 MLog(@"Block", obj); 14 }
console log:
1 2015-08-11 21:59:28.804 Myblock[8755:753300] 指针地址:0x7fff5fbff838, 对象地址:0x100106960, 引用计数:1, 注释:Block 2 2015-08-11 21:59:28.806 Myblock[8755:753300] Stack Block ptr: <__NSStackBlock__: 0x7fff5fbff7d0> 3 2015-08-11 21:59:28.806 Myblock[8755:753300] 指针地址:0x7fff5fbff838, 对象地址:0x100106960, 引用计数:1, 注释:Stack Block 4 2015-08-11 21:59:28.806 Myblock[8755:753300] 指针地址:0x7fff5fbff838, 对象地址:0x100106960, 引用计数:1, 注释:Block
- 总结:未使用__block修饰符,使用了一个指针指向对象,并且引用计数不加1,类似于C++中的引用(别名),而使用__block修饰符,内外部同一个指针指向,并且对象地址不变。
- 2.3.1 未使用__block修饰符
- 3.0 NSMallocBlock
对NSStackBlock执行Block_copy()或copy方法,会返回在heap上的Block引用。当然,Block内外部的作用域依然不变。
- 3.1.1 不使用__block修饰符,访问基本数据类型。
1 void testMallocBlock() 2 { 3 NSInteger var = 1; 4 NSLog(@"address: %p, var: %ld", &var, var); 5 NSLog(@"******************** NSStackBlock start *******************"); 6 void (^stackBlock)(NSString *typeStr) = ^(NSString *typeStr) 7 { 8 NSString *tmp = [NSString stringWithFormat:@"%@ var", typeStr]; 9 NSLog(@"%@, address: %p, var: %ld", tmp, &var, var); 10 }; 11 12 stackBlock(@"StackBlcok"); 13 NSLog(@"Blcok address: %@", stackBlock); 14 NSLog(@"address: %p, var: %ld", &var, var); 15 NSLog(@"******************** NSStackBlock end *******************"); 16 NSLog(@"******************** NSMallocBlock start ****************"); 17 void (^mallocBlock)(NSString *typeStr) = Block_copy(stackBlock); 18 mallocBlock(@"MallocBlock"); 19 NSLog(@"address: %p, var: %ld", &var, var); 20 NSLog(@"Blcok address: %@", mallocBlock); 21 Block_release(mallocBlock); 22 NSLog(@"address: %p, var: %ld", &var, var); 23 NSLog(@"******************** NSMallocBlock end ******************"); 24 25 }
console log:
1 2015-08-12 14:29:53.814 LearnOC[1155:73933] address: 0x7fff5fbff7c8, var: 1 2 2015-08-12 14:29:53.816 LearnOC[1155:73933] ******************** NSStackBlock start ******************* 3 2015-08-12 14:29:53.816 LearnOC[1155:73933] StackBlcok var, address: 0x7fff5fbff7b8, var: 1 4 2015-08-12 14:29:53.817 LearnOC[1155:73933] Blcok address: <__NSStackBlock__: 0x7fff5fbff798> 5 2015-08-12 14:29:53.817 LearnOC[1155:73933] address: 0x7fff5fbff7c8, var: 1 6 2015-08-12 14:29:53.817 LearnOC[1155:73933] ******************** NSStackBlock end ******************* 7 2015-08-12 14:29:53.817 LearnOC[1155:73933] ******************** NSMallocBlock start **************** 8 2015-08-12 14:29:53.818 LearnOC[1155:73933] MallocBlock var, address: 0x100400370, var: 1 9 2015-08-12 14:29:53.818 LearnOC[1155:73933] address: 0x7fff5fbff7c8, var: 1 10 2015-08-12 14:29:53.818 LearnOC[1155:73933] Blcok address: <__NSMallocBlock__: 0x100400350> 11 2015-08-12 14:29:53.818 LearnOC[1155:73933] address: 0x7fff5fbff7c8, var: 1 12 2015-08-12 14:29:53.819 LearnOC[1155:73933] ******************** NSMallocBlock end ******************
总结:将栈Block转成堆Block,变量作用域不变,就是将变量改成在堆上存储。不使用__block修饰变量,在Block体中将数据进行拷贝,类似于C++中的函数参数值传递。
- 3.1.2 使用__block修饰符,访问数据类型变量
1 void testMallocBlock() 2 { 3 __block NSInteger var = 1; 4 NSLog(@"address: %p, var: %ld", &var, var); 5 NSLog(@"******************** NSStackBlock start *******************"); 6 void (^stackBlock)(NSString *typeStr) = ^(NSString *typeStr) 7 { 8 NSString *tmp = [NSString stringWithFormat:@"%@ var", typeStr]; 9 NSLog(@"%@, address: %p, var: %ld", tmp, &var, var); 10 }; 11 12 stackBlock(@"StackBlcok"); 13 NSLog(@"Blcok address: %@", stackBlock); 14 NSLog(@"address: %p, var: %ld", &var, var); 15 NSLog(@"******************** NSStackBlock end *******************"); 16 NSLog(@"******************** NSMallocBlock start ****************"); 17 void (^mallocBlock)(NSString *typeStr) = Block_copy(stackBlock); 18 mallocBlock(@"MallocBlock"); 19 NSLog(@"address: %p, var: %ld", &var, var); 20 NSLog(@"Blcok address: %@", mallocBlock); 21 Block_release(mallocBlock); 22 NSLog(@"address: %p, var: %ld", &var, var); 23 NSLog(@"******************** NSMallocBlock end ******************"); 24 25 }
console log:
1 2015-08-12 14:31:58.951 LearnOC[1165:74807] address: 0x7fff5fbff7c8, var: 1 2 2015-08-12 14:31:58.952 LearnOC[1165:74807] ******************** NSStackBlock start ******************* 3 2015-08-12 14:31:58.953 LearnOC[1165:74807] StackBlcok var, address: 0x7fff5fbff7c8, var: 1 4 2015-08-12 14:31:58.953 LearnOC[1165:74807] Blcok address: <__NSStackBlock__: 0x7fff5fbff770> 5 2015-08-12 14:31:58.953 LearnOC[1165:74807] address: 0x7fff5fbff7c8, var: 1 6 2015-08-12 14:31:58.953 LearnOC[1165:74807] ******************** NSStackBlock end ******************* 7 2015-08-12 14:31:58.954 LearnOC[1165:74807] ******************** NSMallocBlock start **************** 8 2015-08-12 14:31:58.954 LearnOC[1165:74807] MallocBlock var, address: 0x10010ccb8, var: 1 9 2015-08-12 14:31:58.954 LearnOC[1165:74807] address: 0x10010ccb8, var: 1 10 2015-08-12 14:31:58.954 LearnOC[1165:74807] Blcok address: <__NSMallocBlock__: 0x100107090> 11 2015-08-12 14:31:58.954 LearnOC[1165:74807] address: 0x10010ccb8, var: 1 12 2015-08-12 14:31:58.955 LearnOC[1165:74807] ******************** NSMallocBlock end ******************
总结:使用Block_copy方法,将stack Blcok转成heap Block,即block指针从栈中转移到堆。使用__block修饰变量,变量作用域不变,在Block内外部使用的数据相同(数据的地址一致),类似于C++中的函数参数的引用(别名)。
- 3.2 NSMallocBlock访问对象类型
- 3.2.1 访问未使用__block修饰符修饰对象
1 void testMallocBlockParamO() 2 { 3 NSObject *obj = [[NSObject alloc] init]; 4 MyLog(@"obj", obj); 5 NSLog(@"******************** NSStackBlock start *******************"); 6 void (^stackBlock)(NSString *typeStr) = ^(NSString *typeStr) 7 { 8 NSString *tmp = [NSString stringWithFormat:@"%@ obj", typeStr]; 9 MyLog(tmp, obj); 10 }; 11 12 stackBlock(@"StackBlcok"); 13 NSLog(@"Blcok address: %@", stackBlock); 14 MyLog(@"obj", obj); 15 NSLog(@"******************** NSStackBlock end *******************"); 16 NSLog(@"******************** NSMallocBlock start ****************"); 17 void (^mallocBlock)(NSString *typeStr) = Block_copy(stackBlock); 18 mallocBlock(@"MallocBlock"); 19 MyLog(@"obj", obj); 20 NSLog(@"Blcok address: %@", mallocBlock); 21 Block_release(mallocBlock); 22 MyLog(@"obj", obj); 23 NSLog(@"******************** NSMallocBlock end ******************"); 24 }
console log:
1 2015-08-12 14:22:36.711 LearnOC[1128:72001] 指针地址:0x7fff5fbff7c8, 数据地址:0x100100080, 对象地址:0x100100080, 引用计数:1, 注释:obj 2 2015-08-12 14:22:36.713 LearnOC[1128:72001] ******************** NSStackBlock start ******************* 3 2015-08-12 14:22:36.713 LearnOC[1128:72001] 指针地址:0x7fff5fbff7b8, 数据地址:0x100100080, 对象地址:0x100100080, 引用计数:1, 注释:StackBlcok obj 4 2015-08-12 14:22:36.713 LearnOC[1128:72001] Blcok address: <__NSStackBlock__: 0x7fff5fbff798> 5 2015-08-12 14:22:36.714 LearnOC[1128:72001] 指针地址:0x7fff5fbff7c8, 数据地址:0x100100080, 对象地址:0x100100080, 引用计数:1, 注释:obj 6 2015-08-12 14:22:36.714 LearnOC[1128:72001] ******************** NSStackBlock end ******************* 7 2015-08-12 14:22:36.714 LearnOC[1128:72001] ******************** NSMallocBlock start **************** 8 2015-08-12 14:22:36.714 LearnOC[1128:72001] 指针地址:0x100213d40, 数据地址:0x100100080, 对象地址:0x100100080, 引用计数:2, 注释:MallocBlock obj 9 2015-08-12 14:22:36.714 LearnOC[1128:72001] 指针地址:0x7fff5fbff7c8, 数据地址:0x100100080, 对象地址:0x100100080, 引用计数:2, 注释:obj 10 2015-08-12 14:22:36.715 LearnOC[1128:72001] Blcok address: <__NSMallocBlock__: 0x100213d20> 11 2015-08-12 14:22:36.722 LearnOC[1128:72001] 指针地址:0x7fff5fbff7c8, 数据地址:0x100100080, 对象地址:0x100100080, 引用计数:1, 注释:obj 12 2015-08-12 14:22:36.722 LearnOC[1128:72001] ******************** NSMallocBlock end ******************
总结:使用Block_copy方法,将stack Blcok转成heap Block,即block指针从栈中转移到堆。在Block体内的变量作用域不变,Block存储于堆内存,对象进行拷贝(类似于C++函数参数值传递),将指针存储的对象的地址拷贝到新申请的地址中,对象地址不变,这个过程有点像智能指针,其实就是IOS中的引用,并且引用计数加1。通过Block_release()方法,将Block体中的对象引用计数减1。
- 3.2.2 访问对象使用__block修饰
1 void testMallocBlockParamO() 2 { 3 __block NSObject *obj = [[NSObject alloc] init]; 4 MyLog(@"obj", obj); 5 NSLog(@"******************** NSStackBlock start *******************"); 6 void (^stackBlock)(NSString *typeStr) = ^(NSString *typeStr) 7 { 8 NSString *tmp = [NSString stringWithFormat:@"%@ obj", typeStr]; 9 MyLog(tmp, obj); 10 }; 11 12 stackBlock(@"StackBlcok"); 13 NSLog(@"Blcok address: %@", stackBlock); 14 MyLog(@"obj", obj); 15 NSLog(@"******************** NSStackBlock end *******************"); 16 NSLog(@"******************** NSMallocBlock start ****************"); 17 void (^mallocBlock)(NSString *typeStr) = Block_copy(stackBlock); 18 mallocBlock(@"MallocBlock"); 19 MyLog(@"obj", obj); 20 NSLog(@"Blcok address: %@", mallocBlock); 21 Block_release(mallocBlock); 22 MyLog(@"obj", obj); 23 NSLog(@"******************** NSMallocBlock end ******************"); 24 }
console log:
1 2015-08-12 14:33:15.265 LearnOC[1173:75294] 指针地址:0x7fff5fbff7c8, 数据地址:0x1002068f0, 对象地址:0x1002068f0, 引用计数:1, 注释:obj 2 2015-08-12 14:33:15.267 LearnOC[1173:75294] ******************** NSStackBlock start ******************* 3 2015-08-12 14:33:15.267 LearnOC[1173:75294] 指针地址:0x7fff5fbff7c8, 数据地址:0x1002068f0, 对象地址:0x1002068f0, 引用计数:1, 注释:StackBlcok obj 4 2015-08-12 14:33:15.267 LearnOC[1173:75294] Blcok address: <__NSStackBlock__: 0x7fff5fbff760> 5 2015-08-12 14:33:15.268 LearnOC[1173:75294] 指针地址:0x7fff5fbff7c8, 数据地址:0x1002068f0, 对象地址:0x1002068f0, 引用计数:1, 注释:obj 6 2015-08-12 14:33:15.268 LearnOC[1173:75294] ******************** NSStackBlock end ******************* 7 2015-08-12 14:33:15.268 LearnOC[1173:75294] ******************** NSMallocBlock start **************** 8 2015-08-12 14:33:15.268 LearnOC[1173:75294] 指针地址:0x100206d28, 数据地址:0x1002068f0, 对象地址:0x1002068f0, 引用计数:1, 注释:MallocBlock obj 9 2015-08-12 14:33:15.268 LearnOC[1173:75294] 指针地址:0x100206d28, 数据地址:0x1002068f0, 对象地址:0x1002068f0, 引用计数:1, 注释:obj 10 2015-08-12 14:33:15.269 LearnOC[1173:75294] Blcok address: <__NSMallocBlock__: 0x1002069c0> 11 2015-08-12 14:33:15.290 LearnOC[1173:75294] 指针地址:0x100206d28, 数据地址:0x1002068f0, 对象地址:0x1002068f0, 引用计数:1, 注释:obj 12 2015-08-12 14:33:15.291 LearnOC[1173:75294] ******************** NSMallocBlock end ******************
总结:使用Block_copy方法,将stack Blcok转成heap Block,即block指针从栈中转移到堆。同时,Blcok中对象也是一个,在heap申请地址存储对象地址。由于,对象使用__block修饰符修饰,在堆中,对象内外部地址一致,引用计数不加。
- 3.2.1 访问未使用__block修饰符修饰对象
- 4.0 在MRC模式下,Block循环引用问题
- 4.1 Block 访问对象数据类型
- 4.1.2 在未使用__block修饰符情景下:
1 - (void)testMallocBlock 2 { 3 NSObject *objc = [[NSObject alloc] init]; 4 MLog(@"self", self); 5 MLog(@"objc", objc); 6 7 NSLog(@"******************** NSStackBlock start *******************"); 8 MallocBlock stackBlk = ^(NSString *typeStr) 9 { 10 NSString *tmpSelf = [NSString stringWithFormat:@"%@ self", typeStr]; 11 NSString *tmpObjc = [NSString stringWithFormat:@"%@ objc", typeStr]; 12 MLog(tmpSelf, self); 13 MLog(tmpObjc, objc); 14 }; 15 16 stackBlk(@"StackBlock"); 17 MLog(@"Block objc", objc); 18 MLog(@"self", self); 19 NSLog(@"Stack Block obj: %@", stackBlk); 20 NSLog(@"******************** NSStackBlock end *******************"); 21 22 NSLog(@"******************** NSMallocBlock start *******************"); 23 MallocBlock mallocBlk = Block_copy(stackBlk); 24 mallocBlk(@"MallocBlock"); 25 MLog(@"objc", objc); 26 MLog(@"self", self); 27 Block_release(mallocBlk); 28 MLog(@"objc", objc); 29 MLog(@"self", self); 30 NSLog(@"Malloc Block obj: %@", mallocBlk); 31 NSLog(@"******************** NSMallocBlock end *******************"); 32 }
console log:
1 2015-08-12 13:52:45.936 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7b8, 对象地址: 0x100206a20, 对象: <CRMallocBlock: 0x100206a20>, 引用计数: 1, 注释: self 2 2015-08-12 13:52:45.937 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x100206a80, 对象: <NSObject: 0x100206a80>, 引用计数: 1, 注释: objc 3 2015-08-12 13:52:45.937 LearnOC[1107:65975] ******************** NSStackBlock start ******************* 4 2015-08-12 13:52:47.893 LearnOC[1107:65975] 指针地址: 0x7fff5fbff790, 对象地址: 0x100206a20, 对象: <CRMallocBlock: 0x100206a20>, 引用计数: 1, 注释: StackBlock self 5 2015-08-12 13:52:47.894 LearnOC[1107:65975] 指针地址: 0x7fff5fbff798, 对象地址: 0x100206a80, 对象: <NSObject: 0x100206a80>, 引用计数: 1, 注释: StackBlock objc 6 2015-08-12 13:52:47.894 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x100206a80, 对象: <NSObject: 0x100206a80>, 引用计数: 1, 注释: Block objc 7 2015-08-12 13:52:47.895 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7b8, 对象地址: 0x100206a20, 对象: <CRMallocBlock: 0x100206a20>, 引用计数: 1, 注释: self 8 2015-08-12 13:52:47.895 LearnOC[1107:65975] Stack Block obj: <__NSStackBlock__: 0x7fff5fbff770> 9 2015-08-12 13:52:47.895 LearnOC[1107:65975] ******************** NSStackBlock end ******************* 10 2015-08-12 13:52:47.895 LearnOC[1107:65975] ******************** NSMallocBlock start ******************* 11 2015-08-12 13:52:47.896 LearnOC[1107:65975] 指针地址: 0x100206f00, 对象地址: 0x100206a20, 对象: <CRMallocBlock: 0x100206a20>, 引用计数: 2, 注释: MallocBlock self 12 2015-08-12 13:52:47.896 LearnOC[1107:65975] 指针地址: 0x100206f08, 对象地址: 0x100206a80, 对象: <NSObject: 0x100206a80>, 引用计数: 2, 注释: MallocBlock objc 13 2015-08-12 13:52:47.896 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x100206a80, 对象: <NSObject: 0x100206a80>, 引用计数: 2, 注释: objc 14 2015-08-12 13:52:47.897 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7b8, 对象地址: 0x100206a20, 对象: <CRMallocBlock: 0x100206a20>, 引用计数: 2, 注释: self 15 2015-08-12 13:52:47.897 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x100206a80, 对象: <NSObject: 0x100206a80>, 引用计数: 1, 注释: objc 16 2015-08-12 13:52:47.897 LearnOC[1107:65975] 指针地址: 0x7fff5fbff7b8, 对象地址: 0x100206a20, 对象: <CRMallocBlock: 0x100206a20>, 引用计数: 1, 注释: self 17 2015-08-12 13:52:47.898 LearnOC[1107:65975] Malloc Block obj: <__NSMallocBlock__: 0x100206ee0> 18 2015-08-12 13:52:47.899 LearnOC[1107:65975] ******************** NSMallocBlock end *******************
总结:结合2节和3节,在此主要说对象反复引用问题,在栈中的Block对使用的外部对象,进行拷贝,存储于栈中,对象引用计数未变,而通过Block_copy或copy方法,将Block拷贝到堆中,同时,Block使用的外部对象,进行拷贝,此时,存储于堆中,由于,未使用__block修饰,在Block体中申请地址存储对象,以致于,内外部指针对象的指针不同,导致引用计数加1。此时,要使用Blcok_release释放引用,让引用计数减1。
- 4.1.3 在使用__block修饰符情景下:
1 - (void)testMallocBlock 2 { 3 __block NSObject *objc = [[NSObject alloc] init]; 4 __block CRMallocBlock *SelfO = self; 5 MLog(@"self", SelfO); 6 MLog(@"objc", objc); 7 8 NSLog(@"******************** NSStackBlock start *******************"); 9 MallocBlock stackBlk = ^(NSString *typeStr) 10 { 11 NSString *tmpSelf = [NSString stringWithFormat:@"%@ self", typeStr]; 12 NSString *tmpObjc = [NSString stringWithFormat:@"%@ objc", typeStr]; 13 MLog(tmpSelf, SelfO); 14 MLog(tmpObjc, objc); 15 }; 16 17 stackBlk(@"StackBlock"); 18 MLog(@"Block objc", objc); 19 MLog(@"self", SelfO); 20 NSLog(@"Stack Block obj: %@", stackBlk); 21 NSLog(@"******************** NSStackBlock end *******************"); 22 23 NSLog(@"******************** NSMallocBlock start *******************"); 24 MallocBlock mallocBlk = Block_copy(stackBlk); 25 mallocBlk(@"MallocBlock"); 26 MLog(@"objc", objc); 27 MLog(@"self", SelfO); 28 Block_release(mallocBlk); 29 MLog(@"objc", objc); 30 MLog(@"self", SelfO); 31 NSLog(@"Malloc Block obj: %@", mallocBlk); 32 NSLog(@"******************** NSMallocBlock end *******************"); 33 }
console log:
1 2015-08-12 14:02:36.271 LearnOC[1115:68031] 指针地址: 0x7fff5fbff778, 对象地址: 0x100113880, 对象: <CRMallocBlock: 0x100113880>, 引用计数: 1, 注释: self 2 2015-08-12 14:02:36.273 LearnOC[1115:68031] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x1001138e0, 对象: <NSObject: 0x1001138e0>, 引用计数: 1, 注释: objc 3 2015-08-12 14:02:36.273 LearnOC[1115:68031] ******************** NSStackBlock start ******************* 4 2015-08-12 14:02:53.842 LearnOC[1115:68031] 指针地址: 0x7fff5fbff778, 对象地址: 0x100113880, 对象: <CRMallocBlock: 0x100113880>, 引用计数: 1, 注释: StackBlock self 5 2015-08-12 14:02:53.842 LearnOC[1115:68031] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x1001138e0, 对象: <NSObject: 0x1001138e0>, 引用计数: 1, 注释: StackBlock objc 6 2015-08-12 14:02:54.588 LearnOC[1115:68031] 指针地址: 0x7fff5fbff7a8, 对象地址: 0x1001138e0, 对象: <NSObject: 0x1001138e0>, 引用计数: 1, 注释: Block objc 7 2015-08-12 14:02:57.452 LearnOC[1115:68031] 指针地址: 0x7fff5fbff778, 对象地址: 0x100113880, 对象: <CRMallocBlock: 0x100113880>, 引用计数: 1, 注释: self 8 2015-08-12 14:02:58.440 LearnOC[1115:68031] Stack Block obj: <__NSStackBlock__: 0x7fff5fbff708> 9 2015-08-12 14:02:59.073 LearnOC[1115:68031] ******************** NSStackBlock end ******************* 10 2015-08-12 14:03:33.209 LearnOC[1115:68031] ******************** NSMallocBlock start ******************* 11 2015-08-12 14:03:35.225 LearnOC[1115:68031] 指针地址: 0x100300138, 对象地址: 0x100113880, 对象: <CRMallocBlock: 0x100113880>, 引用计数: 1, 注释: MallocBlock self 12 2015-08-12 14:03:35.225 LearnOC[1115:68031] 指针地址: 0x100300c48, 对象地址: 0x1001138e0, 对象: <NSObject: 0x1001138e0>, 引用计数: 1, 注释: MallocBlock objc 13 2015-08-12 14:03:37.700 LearnOC[1115:68031] 指针地址: 0x100300c48, 对象地址: 0x1001138e0, 对象: <NSObject: 0x1001138e0>, 引用计数: 1, 注释: objc 14 2015-08-12 14:03:45.868 LearnOC[1115:68031] 指针地址: 0x100300138, 对象地址: 0x100113880, 对象: <CRMallocBlock: 0x100113880>, 引用计数: 1, 注释: self 15 2015-08-12 14:04:06.276 LearnOC[1115:68031] 指针地址: 0x100300c48, 对象地址: 0x1001138e0, 对象: <NSObject: 0x1001138e0>, 引用计数: 1, 注释: objc 16 2015-08-12 14:04:07.235 LearnOC[1115:68031] 指针地址: 0x100300138, 对象地址: 0x100113880, 对象: <CRMallocBlock: 0x100113880>, 引用计数: 1, 注释: self 17 2015-08-12 14:04:07.993 LearnOC[1115:68031] Malloc Block obj: <__NSMallocBlock__: 0x100300000>
总结:结合2、3节内容,NSMallocBlock指向内外部对象的指针是同一个,所以,引用计数不加1。
- 4.1.2 在未使用__block修饰符情景下:
2. 在ARC模式下,Block的分类NSGlobalBlock和NSMallocBlock两类。
- 1.0 概述
- 在ARC模式下,Block除了NSGlobalBlock类型,其它Block都会被拷贝到堆上。
- 2.0 NSGlobalBlock
- 在ARC模式下的NSGlobalBlock与MRC模式下NSGlobalBlock一致。
- 3.0 NSMallocBlock
- 在ARC模式下的NSMallocBlock与MRC模式下NSMallocBlock一致。
- 4.0 在ARC模式下,循环引用问题
- 在ARC模式下,Block对外部对象的循环引用问题,可以通过__weak解决
1 __weak NSObject *objc = [[NSObject alloc] init]; 2 __weak CRMallocBlock *SelfO = self;
- 5.0 在ARC模式下,使用Property定义Block,属性修饰符使用copy。
1 @property (nonatomic, copy) void (^blockname)(void);