关于block使用的5点注意事项
1、在使用block前需要对block指针做判空处理。
不判空直接使用,一旦指针为空直接产生崩溃。
if(!self.isOnlyNet) {if(succBlock == NULL) {//后面使用block之前要先做判空处理return;
}iddata =[NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]];if([data isKindOfClass:[NSMutableArrayclass]]) {
succBlock(data,YES);
}else{
succBlock(nil,YES);
}
}
2、在MRC的编译环境下,block如果作为成员参数要copy一下将栈上的block拷贝到堆上(示例见下,原因参考)
3、在block使用之后要对,block指针做赋空值处理,如果是MRC的编译环境下,要先release掉block对象。
block作为类对象的成员变量,使用block的人有可能用类对象参与block中的运算而产生循环引用。
将block赋值为空,是解掉循环引用的重要方法。(不能只在dealloc里面做赋空值操作,这样已经产生的循环引用不会被破坏掉)
typedefvoid(^SuccBlock)(iddata);@interfaceNetworkClass {
SuccessBlock _sucBlock;
}
@property (nonatomic,assign)BOOL propertyUseInCallBack;- (void) requestWithSucBlock: (SuccessBlock) callbackBlock;@end@implementationNetworkClass- (void) requestWithSucBlock: (SuccessBlock) callbackBlock {
_sucBlock= callbackBlock;//MRC下:_sucBlock = [callbackBlock copy]; 不copy block会在栈上被回收。}- (void) netwrokDataBack: (id) data {if(data != nil && _sucBlock !=NULL) {
_sucBlock(data);
}//MRC下:要先将[_sucBlock release];(之前copy过)_sucBlock = nil;//Importent: 在使用之后将Block赋空值,解引用 !!!}@end//=======================以下是使用方===========================@implementationUserCode- (void) temporaryNetworkCall
{
NetworkClass*netObj =[[NetworkClass alloc] init];
netObj.propertyUseInCallBack=NO;
[netObj requestWithSucBlock:^(iddata) {//由于block里面引用netObj的指针所以这里产生了循环引用,且由于这个block是作为参数传入对象的,编译器不会报错。//因此,NetworkClass使用完block之后一定要将作为成员变量的block赋空值。if(netObj.propertyUseInCallBack ==YES) {//Do Something...}
}];
}@end
还有一种改法,在block接口设计时,将可能需要的变量作为形参传到block中,从设计上解决循环引用的问题。
如果上面Network类设计成这个样子:
@classNetowrkClass;
typedefvoid(^SuccBlock)(NetworkClass *aNetworkObj,iddata);@interfaceNetworkClass//...@end@implementationNetworkClass@end@implementationUserCode- (void) temporaryNetworkCall
{
NetworkClass*netObj =[[NetworkClass alloc] init];
netObj.propertyUseInCallBack=NO;
[netObj requestWithSucBlock:^(NetworkClass *aNetworkObj,iddata) {//这里参数中已经有netObj的对象了,使用者不用再从block外引用指针了。if(aNetworkObj.propertyUseInCallBack ==YES) {//Do Something...}
}];
}@end
4、使用方将self或成员变量加入block之前要先将self变为__weak
5、在多线程环境下(block中的weakSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。
第四、第五条合起来有个名词叫weak–strong dance,来自于2011 WWDC Session #322 (Objective-C Advancements in Depth)
以下代码来自AFNetworking,堪称使用weak–strong dance的经典。
__weak __typeof(self)weakSelf =self;
AFNetworkReachabilityStatusBlock callback= ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf=weakSelf;
strongSelf.networkReachabilityStatus=status;if(strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
Review一下上面这段代码,里面玄机不少。
第一行:__weak __typeof(self)weakSelf = self;
如之前第四条所说,为防止callback内部对self强引用,weak一下。
其中用到了__typeof(self),这里涉及几个知识点:
a. __typeof、__typeof__、typeof的区别
恩~~他们没有区别,但是这牵扯一段往事,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。
b.对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
按照之前第五条的说法给转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。
第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。
最后第五行,使用前对block判空。
写在最后,阅读好的开源库源码是提高个人水平的一个很好途径,看见不懂的地方去查去摸索会得到更多。1、在使用block前需要对block指针做判空处理。
不判空直接使用,一旦指针为空直接产生崩溃。
if(!self.isOnlyNet) {if(succBlock == NULL) {//后面使用block之前要先做判空处理return;
}iddata =[NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]];if([data isKindOfClass:[NSMutableArrayclass]]) {
succBlock(data,YES);
}else{
succBlock(nil,YES);
}
}
2、在MRC的编译环境下,block如果作为成员参数要copy一下将栈上的block拷贝到堆上(示例见下,原因参考)
3、在block使用之后要对,block指针做赋空值处理,如果是MRC的编译环境下,要先release掉block对象。
block作为类对象的成员变量,使用block的人有可能用类对象参与block中的运算而产生循环引用。
将block赋值为空,是解掉循环引用的重要方法。(不能只在dealloc里面做赋空值操作,这样已经产生的循环引用不会被破坏掉)
typedefvoid(^SuccBlock)(iddata);@interfaceNetworkClass {
SuccessBlock _sucBlock;
}
@property (nonatomic,assign)BOOL propertyUseInCallBack;- (void) requestWithSucBlock: (SuccessBlock) callbackBlock;@end@implementationNetworkClass- (void) requestWithSucBlock: (SuccessBlock) callbackBlock {
_sucBlock= callbackBlock;//MRC下:_sucBlock = [callbackBlock copy]; 不copy block会在栈上被回收。}- (void) netwrokDataBack: (id) data {if(data != nil && _sucBlock !=NULL) {
_sucBlock(data);
}//MRC下:要先将[_sucBlock release];(之前copy过)_sucBlock = nil;//Importent: 在使用之后将Block赋空值,解引用 !!!}@end//=======================以下是使用方===========================@implementationUserCode- (void) temporaryNetworkCall
{
NetworkClass*netObj =[[NetworkClass alloc] init];
netObj.propertyUseInCallBack=NO;
[netObj requestWithSucBlock:^(iddata) {//由于block里面引用netObj的指针所以这里产生了循环引用,且由于这个block是作为参数传入对象的,编译器不会报错。//因此,NetworkClass使用完block之后一定要将作为成员变量的block赋空值。if(netObj.propertyUseInCallBack ==YES) {//Do Something...}
}];
}@end
还有一种改法,在block接口设计时,将可能需要的变量作为形参传到block中,从设计上解决循环引用的问题。
如果上面Network类设计成这个样子:
@classNetowrkClass;
typedefvoid(^SuccBlock)(NetworkClass *aNetworkObj,iddata);@interfaceNetworkClass//...@end@implementationNetworkClass@end@implementationUserCode- (void) temporaryNetworkCall
{
NetworkClass*netObj =[[NetworkClass alloc] init];
netObj.propertyUseInCallBack=NO;
[netObj requestWithSucBlock:^(NetworkClass *aNetworkObj,iddata) {//这里参数中已经有netObj的对象了,使用者不用再从block外引用指针了。if(aNetworkObj.propertyUseInCallBack ==YES) {//Do Something...}
}];
}@end
4、使用方将self或成员变量加入block之前要先将self变为__weak
5、在多线程环境下(block中的weakSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。
第四、第五条合起来有个名词叫weak–strong dance,来自于2011 WWDC Session #322 (Objective-C Advancements in Depth)
以下代码来自AFNetworking,堪称使用weak–strong dance的经典。
__weak __typeof(self)weakSelf =self;
AFNetworkReachabilityStatusBlock callback= ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf=weakSelf;
strongSelf.networkReachabilityStatus=status;if(strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
Review一下上面这段代码,里面玄机不少。
第一行:__weak __typeof(self)weakSelf = self;
如之前第四条所说,为防止callback内部对self强引用,weak一下。
其中用到了__typeof(self),这里涉及几个知识点:
a. __typeof、__typeof__、typeof的区别
恩~~他们没有区别,但是这牵扯一段往事,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。
b.对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
按照之前第五条的说法给转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。
第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。
最后第五行,使用前对block判空。
写在最后,阅读好的开源库源码是提高个人水平的一个很好途径,看见不懂的地方去查去摸索会得到更多。