Block

一、在使用block前须要对block指针作判空处理。html

不判空直接使用,一旦指针为空直接产生崩溃。ios

复制代码
if (!self.isOnlyNet) {
    if (succBlock == NULL) { //后面使用block以前要先作判空处理
        return;
    }
    id data = [NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]];
    if ([data isKindOfClass:[NSMutableArray class]]) {
        succBlock(data,YES);
    }else{
        succBlock(nil,YES);
    }
}
复制代码

 

二、在MRC的编译环境下,block若是做为成员参数要copy一下将栈上的block拷贝到堆上(示例见下, 缘由参考

三、在block使用以后要对,block指针作赋空值处理,若是是MRC的编译环境下,要先release掉block对象。web

block做为类对象的成员变量,使用block的人有可能用类对象参与block中的运算而产生循环引用。多线程

将block赋值为空,是解掉循环引用的重要方法。(不能只在dealloc里面作赋空值操做,这样已经产生的循环引用不会被破坏掉)google

复制代码
typedef void(^SuccBlock)(id data);
@interface NetworkClass {
    SuccessBlock _sucBlock;
}
@property (nonatomic,assign)BOOL propertyUseInCallBack;
- (void) requestWithSucBlock: (SuccessBlock) callbackBlock;
@end
 
@implementation NetworkClass
- (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
 
//=======================如下是使用方===========================
@implementation UserCode
- (void) temporaryNetworkCall
{
    NetworkClass *netObj = [[NetworkClass alloc] init];
    netObj.propertyUseInCallBack = NO;
    [netObj requestWithSucBlock: ^(id data) {
        //因为block里面引用netObj的指针因此这里产生了循环引用,且因为这个block是做为参数传入对象的,编译器不会报错。
        //所以,NetworkClass使用完block以后必定要将做为成员变量的block赋空值。
        if (netObj.propertyUseInCallBack == YES) {
            //Do Something...
        }
    }];
}
@end
复制代码

 

还有一种改法,在block接口设计时,将可能须要的变量做为形参传到block中,从设计上解决循环引用的问题。atom

若是上面Network类设计成这个样子:url

复制代码
@class NetowrkClass;
typedef void(^SuccBlock)(NetworkClass *aNetworkObj, id data);
@interface NetworkClass
//...
@end
@implementation NetworkClass
@end
 
@implementation UserCode
- (void) temporaryNetworkCall
{
    NetworkClass *netObj = [[NetworkClass alloc] init];
    netObj.propertyUseInCallBack = NO;
    [netObj requestWithSucBlock: ^(NetworkClass *aNetworkObj, id data) {
        //这里参数中已经有netObj的对象了,使用者不用再从block外引用指针了。
        if (aNetworkObj.propertyUseInCallBack == YES) {
            //Do Something...
        }
    }];
}
@end
复制代码

 

四、使用方将self或成员变量加入block以前要先将self变为__weakspa

五、在多线程环境下(block中的weakSelf有可能被析构的状况下),须要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。.net

第4、第五条合起来有个名词叫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 不会被循环引用。

第4、5、六行,若是不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行以后self的对象可能被析构掉,而后后面的StausBlock没有执行,致使逻辑错误。

最后第五行,使用前对block判空。

 

写在最后,阅读好的开源库源码是提升我的水平的一个很好途径,看见不懂的地方去查去摸索会获得更多。

相关文章
相关标签/搜索