iOS开发——项目实战总结&Block使用注意点浅析
Block使用注意点浅析
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);
}
}

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

第一行:__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判空。
写在最后,阅读好的开源库源码是提高个人水平的一个很好途径,看见不懂的地方去查去摸索会得到更多。
iOS开发——项目实战总结&Block使用注意点浅析的更多相关文章
- iOS开发项目实战——Swift实现图片轮播与浏览
近期開始开发一个新的iOS应用,自己决定使用Swift.进行了几天之后,发现了一个非常严峻的问题.那就是无论是书籍,还是网络资源,关于Swift的实在是太少了,随便一搜全都是OC实现某某某功能.就算是 ...
- iOS开发项目实战——Swift实现ScrollView滚动栏功能
手机作为一个小屏设备,须要显示的信息往往无法在一个屏幕上显示,此时就须要使用到滚动栏,当然除了像TableView这样能够自带滚动功能的. 假设一个界面上View较多,那就必须要使用到ScrollVi ...
- iOS开发——项目实战总结&经典错误一
经典错误一 No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=armv7, VA 运行报错 出现的原因:armv7s ...
- IOS开发-项目实战-点赞功能的实现
实现思路: 1.每一条新闻就是一个cell,在cell上添加点赞按钮. 2.让cell的控制器成为自定义cell的代理,将点击了哪一个cell放在代理方法中传出去. 3.并将这条新闻的ID和当前用户的 ...
- iOS开发——项目实战总结&带你看看Objective-C的精髓
带你看看Objective-C的精髓 1:接口与实现 @interface...@end @implementation...@end @class 接口(头文件) 实现文件 向前引用 注:类别通过增 ...
- iOS开发——项目实战总结&关于随机量
关于随机量 rand 是一个标准的 C 函数. random 是定义为 POSIX 标准的一部分. arc4random 是在 BSD 和派生平台. 随机数 arc4random_uniform(N) ...
- iOS开发——项目实战总结&数据持久化分析
数据持久化分析 plist文件(属性列表) preference(偏好设置) NSKeyedArchiver(归档) SQLite 3 CoreData 当存储大块数据时你会怎么做? 你有很多选择,比 ...
- iOS开发——项目实战OC篇&类QQ黏性按钮(封装)
类QQ粘性按钮(封装) 那个,先来说说原理吧: 这里原理就是,在界面设置两个控件一个按钮在上面,一个View在下面(同样大小),当我们拖动按钮的时候显示下面的View,view不移动,但是会根据按钮中 ...
- hyperledger 儿童车级开发项目实战----投票系统(1)
今天根据hyperledger 企业级开发项目实战视频,自己做了一个投票demo.在这做个记录 首先编写智能合约 在$GOPATH的的src路径下创建项目的名称,我的是mkdir vote 然后创建c ...
随机推荐
- C#设计模式(6)——原型模式(Prototype Pattern)
一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...
- Http Status 参考
http://tool.oschina.net/commons?type=5 状态码 含义 100 客户端应当继续发送请求.这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝.客户 ...
- AngularJS实战项目(Ⅰ)--含源码
前言 钻研ABP框架的日子,遇到了很多新的知识,因为对自己而言是新知识,所以经常卡在很多地方,迟迟不能有所突破,作为一个稍有上进心的程序员,内心绝对是不服输的,也绝对是不畏困难的,心底必然有这样一股力 ...
- 【译】用Fragment创建动态的界面布局(附Android示例代码)
原文链接:Building a Dynamic UI with Fragments 为了在Android上创建一个动态和多视图的用户界面,你需要封装UI控件和模块化Activity的行为,以便于你能够 ...
- 用Redis打造URL缩短服务
此文章的英文版本已首发于 CodeProject : Building a simple URL shorten service with Redis 阅读文章之前,我建议你先下载源码,一边看文章,一 ...
- SharePoint 基于 REST API使用简介
之前已经介绍了SP2010中支持CSOM的API进行远程访问SharePoint,但是CSOM的API仍然有一定的局限性,首先使用CSOM类库是基于.Net的,因此也将使用CSOM限制在了.Net平台 ...
- Qt之Dialog\widget\ mainwindow的区别和布局管理器 & 分裂器的区别
1.Dialog\widget\ mainwindow的区别 注意mainwindow和widget的区别,mainwindow都工具栏和菜单栏 Dialog and mainwinodws 都是继承 ...
- Essential C++中文版 前言
天啊,这本书竟是如此轻薄短小.我真想大叫一声“哇欧”!C++ Primer 加上索引.扉页.谢词之后,厚达1237 页,而此书却只有薄薄276 页.套句拳击术语,这是一部“轻量级”作品. 每个人都会好 ...
- css3多列显示
columen-width:定义单列显示的宽度.初始值是auto,适用于不可替代的块级元素,行内块元素和单元格,表格元素除外. 目前Webkit引擎支持-webkit-column-width,Moz ...
- MYSQL操作数据表中的记录
36:操作数据表中的记录插入记录 INSERT INTO 表名 VALUES(); 或者INSERT 表名 VALUES(); UPDATE更新记录(单表更新) DELETE删除记录( ...