目录
Block--使用__weak 和__strong修饰符的问题
Block--概念
Block 可以认为是一种匿名函数,使用如下语法声明一个 Block 类型:
returnType (^block_name)(param)
Block--语法
作为变量:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
作为属性:
@property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);
作为参数:
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
作为函数调用的参数:
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
作为typedef:
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
Block--定义
- void(^block1)()=^(){}
- void(^block2)(int)=^(int a){}//如果没有参数,参数可以隐藏,如果有参数,定义的时候必须要写参数,而且必须要有参数变量名
- int(^block3)()=^int{} block返回可以省略,不管有没有返回值,都可以省略
Block--截取变量
如果是局部变量,block是值传递
如果是静态变量,全局变量,_block修饰的变量,block都是指针传递
Block--类型
Objective-C 中 Block 有三种类型:
- NSStackBlock 存储于栈区
- NSGlobalBlock 存储于程序数据区
- NSMallocBlock 存储于堆区
MRC 下
@property (nonatomic,copy) void(^block)();
int value = 10;
void(^blockA)()=^{
NSLog(@"value:%d",value);
};
NSLog(@"MRC 引用计数:%ld, block is: %@",[blackA retainCount],blockA);
void(^blockB)()=^{
NSLog(@"blockB");
};
NSLog(@"MRC 引用计数:%ld, block is: %@",[blackB retainCount],blockB);
_block = [blockA copy];
NSLog(@"MRC 引用计数:%ld, block is: %@",[self.block retainCount],self.block);
[_block retain];
NSLog(@"MRC 引用计数:%ld, block is: %@",[self.block retainCount],self.block);
[_block release];
NSLog(@"MRC 引用计数:%ld, block is: %@",[self.block retainCount],self.block);
打印结果:
MRC 引用计数: 1, block is: <__NSStackBlock__: 0x7fff59038bc8>
MRC 引用计数: 1, block is: <__NSGlobalBlock__: 0x106bc70e0>
MRC 引用计数: 1, block is: <__NSMallocBlock__: 0x610000058330>
MRC 引用计数: 1, block is: <__NSMallocBlock__: 0x610000058330>
MRC 引用计数: 1, block is: <__NSMallocBlock__: 0x610000058330>
可以看到,blockA 和 blockB的差异只在于有没有调用外部变量,这点差异导致他们的类型不同,存储位置不同。
- NSGlobalBlock
block内部没有引用外部局部变量的 Block 类型都是 NSGlobalBlock 类型,存储于全局数据区,由系统管理其内存,retain、copy、release操作都无效。
-
NSStackBlock
block 内部引用外部局部变量,retain、release 操作无效,存储于栈区,变量作用域结束时,其被系统自动释放销毁。MRC环境下,[mutableArray addObject:blockA],(在 ARC 中不用担心此问题,因为 ARC 中会默认将实例化的 block 拷贝到堆上)在其作用域结束后就是函数出栈后,从 mutableArray 中取到的 blockA 已经被回收,变成野指针。正确的做法是先将 blockA copy到堆上,然后加入数组。支持copy,copy 之后生成新的NSMallocBlock类型对象。
-
NSMallocBlock
如上例中的 _block ,[blockA copy]操作后变量类型变为 NSMallocBlock,支持 retain、release,虽然 retainCount 始终是1, 但内存管理器中仍会增加、减少计数,当引用计数为零的时候释放(可多次retain、release 操作验证)。copy 之后不会生成新的对象,只是增加了一次引用,类似 retain,尽量不要对 Block 使用 retain 操作。
ARC 下:
@property (nonatomic, copy ) void(^block)();
int value = 10;
void(^blockA)() = ^{
NSLog(@"value: %d",value);
};
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockA)), blockA);
void(^blockB)() = ^{
NSLog(@"blockB");
};
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockB)), blockB);
_block = blockA;
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)_block)), _block);
打印结果:
ARC 引用计数: 1, block is: <__NSMallocBlock__: 0x6080000536b0>
ARC 引用计数: 1, block is: <__NSGlobalBlock__: 0x106bc7140>
ARC 引用计数: 1, block is: <__NSMallocBlock__: 0x6080000536b0>
所以 Block 属性的特性限定符一般都是 copy,因为其 setter 方法中会拷贝一份新的副本到堆区。
Block--处理循环引用
默认情况下,Block中捕获的变量是不能修改的,如果想修改,需要使用__
block来声明。
__block int a = 21;
对于 id 类型的变量,在MRC情况下,使用 __block id x 不会 retain 变量,而在 ARC情况下则会对变量进行 retain (即和其他捕获的变量相同)。如果不想在 block 中进行 retain 可以使用 __unsafe_unretained __block id x ,不过这样可能会导致野指针出现。更好的办法是使用 __weak 的临时变量。
typeof(self) __weak weakSelf = self;
或者把使用__block修饰的变量设置为nil,以打破引用循环:
__block AViewController * vc = [AViewController new];
vc.completionHandler = ^{
vc = nil;
}
Block--属性用什么修饰
在非 ARC 的情况下,对于 block 类型的属性应该使用 copy,因为 block需要维持其作用域中捕获的变量。在 ARC 中编译器会自动对 block 进行 copy 操作,因此使用 strong 或者 copy 都可以,没有什么区别,但是苹果仍然建议使用 copy 来指明编译器的行为
Block--使用__weak 和__strong修饰符的问题
在block中调用self会引起循环引用,但是在block中需要对weakSelf进行strong,保证代码在执行到block中,self不会被释放,当block执行完后,会自动释放该strongSelf