关于block使用的5点注意事项

1、在使用block前需要对block指针做判空处理。

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

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);
    }
}

2、在MRC的编译环境下,block如果作为成员参数要copy一下将栈上的block拷贝到堆上(示例见下,原因参考

3、在block使用之后要对,block指针做赋空值处理,如果是MRC的编译环境下,要先release掉block对象。

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

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

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中,从设计上解决循环引用的问题。

如果上面Network类设计成这个样子:

@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

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判空。

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

参考:

http://blog.csdn.net/fhbystudy/article/details/17350951

http://stackoverflow.com/questions/10892361/generic-typeof-for-weak-self-references

http://stackoverflow.com/questions/14877415/difference-between-typeof-typeof-and-typeof-objective-c

http://rocry.com/2012/12/18/objective-c-type-of/

http://fuckingblocksyntax.com/ 这是个工具网站,我每次写block的时候都会用到。

时间: 2024-10-24 19:06:29

关于block使用的5点注意事项的相关文章

ios开发之block的使用,及注意事项

转自:http://my.oschina.net/u/1432769/blog/390401 Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事.需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block必须自己管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理 要么导致return cycle内存泄漏要么内存被提前释放导致crash. Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外.词法

关于block使用的几点注意事项

1.在使用block前需要对block指针做判空处理. 不判空直接使用,一旦指针为空直接产生崩溃. if (!self.isOnlyNet) { if (succBlock == NULL) { //后面使用block之前要先做判空处理 return; } id data = [NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]]; if ([data isKindOfClass:[NSMutableArray class]

iOS开发之Block

iOS开发之Block 1.block的含义和作用 UI开发和网络常见功能实现回调, 按钮的事件处理方法是回调方法, 网络下载后的回调处理 (1) 按钮 target-action 一个方法传入按钮中 (2) 表格视图 传入一个指针self, 回调视图控制器中的方法 (3) block 语句块, 解决回调, 理解为"匿名函数", 定义在方法里面 2.block的基本使用(语法) 涉及知识点: 定义block变量,定义block语句块 block参数和返回值 block捕获外部变量(包括

Objective-C( block的使用)

block block用来保存一段代码 block的标志:^ block跟函数很像:可以保存代码.有返回值.有形参.调用方式跟调用方法一样 block内部可以访问外面的变量 默认情况下,block内部不能修改外面的局部变量 给局部变量加上__block关键字,这个局部变量就可以在block内部修改 利用typedef定义block类型 typedef int (^MyBlock)(int, int); 以后就可以利用MyBlock这种类型来定义block变量 MyBlock block; MyB

OC第六天小结

总结 标号 主题 内容 一 autorelease autorelease基本概念/自动释放池/autorelease基本使用 二 autorelease注意事项 注意点/应用场景 三 ARC 什么是ARC/ARC的注意点和优点/ARC的判断原则/ARC机制判断/ARC快速使用 四 ARC下的内存管理 ARC下单对象内存管理/多对象内存管理/循环引用问题/下@property参数 五 ARC和MRC兼容和转换 ARC模式下如何兼容非ARC的类/如何将MRC转换为ARC 六 Category 什么

01-Java学习笔记

本系列笔记由常彦博整理,请知悉 目  录 一. Java技术基础......................................................................................................... 1 1.1编程语言.......................................................................................................

JavaScript 使用误区

1.JavaScript 使用误区 1.1 赋值运算符应用错误 在 JavaScript 程序中如果你在 if 条件语句中使用赋值运算符的等号 (=) 将会产生一个错误结果, 正确的方法是使用比较运算符的两个等号 (==). if 条件语句返回 false (是我们预期的)因为 x 不等于 10: var x = 0; if (x == 10) if 条件语句返回 true (不是我们预期的)因为条件语句执行为 x 赋值 10,10 为 true: var x = 0; if (x = 10)

重要经验五:block作为属性的注意事项

现代ios应用的开发,不使用GCD和block,效率会降低很多,在今年3月份之前,我在block的学习和使用方面,精力和经验都明显不足,在SF有个牛逼同事,不仅自己积累了一套库,而且对这个库持续进行更新和维护,其架构能力和代码水平都不错,他的代码中使用了大量的GCD,为了适应这些技术,我在coding的过程中,开始有意识的多写GCD的代码,刚开始一切顺利,直到我使用block来作为一个对象的属性,这让我纠结了很久. 我遇到的问题是:1,这种属性block,其他类在使用的时候,应该怎么写? 2,b

block注意事项

1.block的声明和注意事项 #import "ZYViewController.h" @interface ZYViewController () @end /*用typedef可以声明一种类型的block*/ //block前加上typedef, 那么就不是一个block指针(变量),而是一种block类型 //给int (^) (int a, int b) 这种block类型 声明了一个别名,叫做Block5 typedef int (^Block5) (int a, int