block和代理的,通知的区别.代理 block的区别用法需要注意些什么

Block编程值得注意的那些事儿
招聘信息:
block 是在 iOS 4 中引入的新特性,它和 C++ 11 中的 lamba 表达式概念相似,有时候也被称为闭包。经过一段时间的使用,我发现要用对用好 block 还是有不少需要注意的地方,今天就来八一八这些值得注意的事儿。
&本文源码下载:
&二,block 注意事项
1,block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝。
如下代码:
-&(void)testAccessVariable&{&&&&&NSInteger&outsideVariable&=&10;&&&&&&&&&&NSMutableArray&*&outsideArray&=&[[NSMutableArray&alloc]&init];&&&&&&&&&&void&(^blockObject)(void)&=&^(void){&&&&&&&&&NSInteger&insideVariable&=&20;&&&&&&&&&KSLog(@"&&>&member&variable&=&%d",&self.memberVariable);&&&&&&&&&KSLog(@"&&>&outside&variable&=&%d",&outsideVariable);&&&&&&&&&KSLog(@"&&>&inside&variable&=&%d",&insideVariable);&&&&&&&&&&&&&&&&&&[outsideArray&addObject:@"AddedInsideBlock"];&&&&&};&&&&&&&&&&outsideVariable&=&30;&&&&&self.memberVariable&=&30;&&&&&&blockObject();&&&&&&&&&&KSLog(@"&&>&%d&items&in&outsideArray",&[outsideArray&count]);&}&
&输出结果为:
>&member&variable&=&30&>&outside&variable&=&10&>&inside&variable&=&20&>&1&items&in&outsideArray&
注意到没?outside 变量的输出值为10,虽然outside变量在定义 block 之后在定义 block 所在的方法 testAccessVariable 中被修改为 20 了。这里的规则就是:blockObject 在实现时会对 outside 变量进行只读拷贝,在 block 块内使用该只读拷贝。因此这里输出的是拷贝时的变量值 10。如果,我们想要让 blockObject 修改或同步使用 outside 变量就需要用 __block 来修饰 outside 变量。
__block NSInteger outsideVariable = 10;
a),在上面的 block 中,我们往 outsideArray 数组中添加了值,但并未修改 outsideArray 自身,这是允许的,因为拷贝的是 outsideArray 自身。&
b),对于 static 变量,全局变量,在 block 中是有读写权限的,因为在 block 的内部实现中,拷贝的是指向这些变量的指针。
c), __block 变量的内部实现要复杂许多,__block 变量其实是一个结构体对象,拷贝的是指向该结构体对象的指针。
2,非内联(inline) block 不能直接访问 self,只能通过将 self 当作参数传递到 block 中才能使用,并且此时的 self 只能通过 setter 或 getter 方法访问其属性,不能使用句点式方法。但内联 block 不受此限制。
typedef&NSString*&(^IntToStringConverter)(id&self,&NSInteger&paramInteger);&-&(NSString&*)&convertIntToString:(NSInteger)paramInteger&&&&&&&&&&&&&&&&&&usingBlockObject:(IntToStringConverter)paramBlockObject&{&&&&&return&paramBlockObject(self,&paramInteger);&}&&typedef&NSString*&(^IntToStringInlineConverter)(NSInteger&paramInteger);&-&(NSString&*)&convertIntToStringInline:(NSInteger)paramInteger&&&&&&&&&&&&&&&&&&usingBlockObject:(IntToStringInlineConverter)paramBlockObject&{&&&&&return&paramBlockObject(paramInteger);&}&&IntToStringConverter&independentBlockObject&=&^(id&self,&NSInteger&paramInteger)&{&&&&&KSLog(@"&>>&self&%@,&memberVariable&%d",&self,&[self&memberVariable]);&&&&&&&&&&NSString&*result&=&[NSString&stringWithFormat:@"%d",&paramInteger];&&&&&KSLog(@"&>>&independentBlockObject&%@",&result);&&&&&return&&};&&-&(void)testAccessSelf&{&&&&&&&&&&&&&&&[self&convertIntToString:20&usingBlockObject:independentBlockObject];&&&&&&&&&&&&&&&&&&&&IntToStringInlineConverter&inlineBlockObject&=&^(NSInteger&paramInteger)&{&&&&&&&&&KSLog(@"&>>&self&%@,&memberVariable&%d",&self,&self.memberVariable);&&&&&&&&&&&&&&&&&&NSString&*result&=&[NSString&stringWithFormat:@"%d",&paramInteger];&&&&&&&&&KSLog(@"&>>&inlineBlockObject&%@",&result);&&&&&&&&&return&&&&&&};&&&&&[self&convertIntToStringInline:20&usingBlockObject:inlineBlockObject];&}&
3,使用 weak&strong dance 技术来避免循环引用
在第二条中,我提到内联 block 可以直接引用 self,但是要非常小心地在 block 中引用 self。因为在一些内联 block 引用 self,可能会导致循环引用。如下例所示:
@interface&KSViewController&()&{&&&&&id&_&}&&@end&&@implementation&KSViewController&&-&(void)viewDidLoad&{&&&&&[super&viewDidLoad];&&&&&&&&&&&&&&&KSTester&*&tester&=&[[KSTester&alloc]&init];&&&&&[tester&run];&&&&&&&&&&_observer&=&[[NSNotificationCenter&defaultCenter]&&&&&&&&&&&&&&&&&&addObserverForName:@"TestNotificationKey"&&&&&&&&&&&&&&&&&&object:nil&queue:nil&usingBlock:^(NSNotification&*n)&{&&&&&&&&&&&&&&&&&&&&&&NSLog(@"%@",&self);&&&&&&&&&&&&&&&&&&}];&}&&-&(void)dealloc&{&&&&&if&(_observer)&{&&&&&&&&&[[NSNotificationCenter&defaultCenter]&removeObserver:_observer];&&&&&}&}&
在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题:
a) 在消息通知 block 中引用到了 self,在这里 self 对象被 block retain,而 _observer 又 retain 该 block的一份拷贝,通知中心又持有 _observer。因此只要 _observer 对象还没有被解除注册,block 就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过 removeObserver 来解除注册以消除通知中心对 _observer/block 的 retain。
b) 同时,_observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。
上面的过程 a) 值得深入分析一下:
苹果官方文档中对 addObserverForName:object:queue:usingBlock: 中的 block 变量说明如下:
The block is copied by the notification center and (the copy) held until the observer registration is removed.
因此,通知中心会拷贝 block 并持有该拷贝直到解除 _observer 的注册。在 ARC 中,在被拷贝的 block 中无论是直接引用 self 还是通过引用 self 的成员变量间接引用 self,该 block 都会 retain self。
这两个问题,可以用 weak&strong dance 技术来解决。该技术在 WWDC 中介绍过:2011 WWDC Session #322 (Objective-C Advancements in Depth)
__weak&KSViewController&*&wself&=&&_observer&=&[[NSNotificationCenter&defaultCenter]&&&&&&&&&&&&&&addObserverForName:@"TestNotificationKey"&&&&&&&&&&&&&&object:nil&queue:nil&usingBlock:^(NSNotification&*n)&{&&&&&&&&&&&&&&&&&&KSViewController&*&sself&=&&&&&&&&&&&&&&&&&&&if&(sself)&{&&&&&&&&&&&&&&&&&&&&&&NSLog(@"%@",&sself);&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&else&{&&&&&&&&&&&&&&&&&&&&&&NSLog(@"&dealloc&before&we&could&run&this&code.");&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&}];&
下面来分析为什么该手法能够起作用。
首先,在 block 之前定义对 self 的一个弱引用 wself,因为是弱引用,所以当 self 被释放时 wself 会变为 nil;然后在 block 中引用该弱应用,考虑到多线程情况,通过使用强引用 sself 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中 self 被释放;然后在之后的 block 块中使用该强引用 sself,注意在使用前要对 sself 进行了 nil 检测,因为多线程环境下在用弱引用 wself 对强引用 sself 赋值时,弱引用 wself 可能已经为 nil 了。
通过这种手法,block 就不会持有 self 的引用,从而打破了循环引用。
扩展:其他还需要注意避免循环引用的地方
与此类似的情况还有 NSTimer。苹果官方文档中提到"Note in particular that run loops retain their timers, so you can release a timer after you have added it to a run loop.",同时在对接口
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
的 target 说明文档中提到:
The object to which to send the message specified by aSelector when the timer fires. The target object is retained by the timer and released when the timer is invalidated.
结合这两处文档说明,我们就知道只要重复性 timer 还没有被 invalidated,target 对象就会被一直持有而不会被释放。因此当你使用 self 当作 target 时,你就不能期望在 dealloc 中 invalidate timer,因为在 timer 没有被invalidate 之前,dealloc 绝不会被调用。因此,需要找个合适的时机和地方来 invalidate timer,但绝不是在 dealloc 中。&
4,block 内存管理分析
block 其实也是一个 NSObject 对象,并且在大多数情况下,block 是分配在栈上面的,只有当 block 被定义为全局变量或 block 块中没有引用任何 automatic 变量时,block 才分配在全局数据段上。 __block 变量也是分配在栈上面的。
在 ARC 下,编译器会自动检测为我们处理了 block 的大部分内存管理,但当将 block 当作方法参数时候,编译器不会自动检测,需要我们手动拷贝该 block 对象。幸运的是,Cocoa 库中的大部分名称中包含&usingBlock&的接口以及 GCD 接口在其接口内部已经进行了拷贝操作,不需要我们再手动处理了。但除此之外的情况,就需要我们手动干预了。
-&(id)&getBlockArray&{&&&&&int&val&=&10;&&&&&return&[[NSArray&alloc]&initWithObjects:&&&&&&&&&&&&&^{&KSLog(@"&&>&block&0:%d",&val);&},&&&&&&&&&&&&&&&&&^{&KSLog(@"&&>&block&1:%d",&val);&},&&&&&&&&&&&&&&&&&nil];&&&&&&&&&&}&&-&(void)testManageBlockMemory&{&&&&&id&obj&=&[self&getBlockArray];&&&&&typedef&void&(^BlockType)(void);&&&&&BlockType&blockObject&=&(BlockType)[obj&objectAtIndex:0];&&&&&blockObject();&}&
执行上面的代码中,在调用 testManageBlockMemory 时,程序会 crash 掉。因为从 getBlockArray 返回的 block 是分配在 stack 上的,但超出了定义 block 所在的作用域,block 就不在了。正确的做法(被屏蔽的那段代码)是在将 block 添加到 NSArray 中时先 copy 到 heap 上,这样就可以在之后的使用中正常访问。
在 ARC 下,对 block 变量进行 copy 始终是安全的,无论它是在栈上,还是全局数据段,还是已经拷贝到堆上。对栈上的 block 进行 copy 是将它拷贝到堆上;对全局数据段中的 block 进行 copy 不会有任何作用;对堆上的 block 进行 copy 只是增加它的引用记数。
如果栈上的 block 中引用了__block 类型的变量,在将该 block 拷贝到堆上时也会将 __block 变量拷贝到堆上如果该 __block 变量在堆上还没有对应的拷贝的话,否则就增加堆上对应的拷贝的引用记数。
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量11846点击量6082点击量4466点击量3781点击量3163点击量2962点击量2914点击量2648点击量2342
&2016 Chukong Technologies,Inc.
京公网安备89扫二维码下载作业帮
拍照搜题,秒出答案,一键查看所有搜题记录
下载作业帮安装包
扫二维码下载作业帮
拍照搜题,秒出答案,一键查看所有搜题记录
“去查一下顾客的BLOCK ” 什么是Block的含义?
扫二维码下载作业帮
拍照搜题,秒出答案,一键查看所有搜题记录
Block,大楼,大厦;街区.可能是问你客户地址.但不排除你听错了,有可能是BLOG,博客的意思.
为您推荐:
其他类似问题
应该是“顾客的BLOG”,BLOG是博客的意思
Block在国外一般指“街道”,“大厦”,可能是让你查一下顾客的地址
扫描下载二维码delegate block
通知的区别 - 博客频道 - CSDN.NET
cleargreen的博客
delegate:
1,“一对一”,对同一个协议,一个对象只能设置一个代理delegate,所以单例对象就不能用代理;
2,代理更注重过程信息的传输:比如发起一个网络请求,可能想要知道此时请求是否已经开始、是否收到了数据、数据是否已经接受完成、数据接收失败.
1:写法更简练,不需要写protocol、函数等等
2,block注重结果的传输:比如对于一个事件,只想知道成功或者失败,并不需要知道进行了多少或者额外的一些信息
3,block需要注意防止循环引用:
ARC下这样防止:
__weak typeof(self) weakSelf =
& &[yourBlock:^(NSArray *repeatedArray, NSArray *incompleteArray) {
&&&&&& [weakSelf doSomething];
__block typeof(self) weakSelf =
& &[yourBlock:^(NSArray *repeatedArray, NSArray *incompleteArray) {
&&&&&& [weakSelf doSomething];
delegate运行成本低。block成本很高的。
block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除;delegate只是保存了一个对象指针,直接回调,没有额外消耗。相对C的函数指针,只多做了一个查表动作
消息能实现一对多,使用场合较多,比如APPDelegate将推送消息发给多个ViewController,但是一定要注意,一旦接收消息的对象多了,就难以控制了,可能有你不希望的对象接受了消息并做了处理,另外一旦注册了观察者,在dealloc里面一定要移除;
cleargreen
排名:千里之外
(2)(9)(1)(3)(3)(3)1509人阅读
文/yeahming(简书作者)
原文链接:/p/
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
首先说一下在OC语言中消息通知的常用几种方式。
第一就是代理,这也是很常用的方式,特点是一对一的形式,而且逻辑结构非常清晰。实现起来较为简单:写协议 ,设置代理这个属性, &最好在你想通知代理做事情的方法中调用即可。当然这里面有一些细节,包括 ①协议定义时,请用关键字@required,和@optional来明确代理是否必须实现某些方法 ②代理的类型需用id类型,并写明要遵守的协议 ③就是在调用代理方法的时候需要判断代理是否实现该方法。
第二就是通知,通知的优点很明显,他是一对多的形式,而且可以在任意对象之间传递,不需要二者有联系,当然他的实现和代理相比较稍微绕一点,注册,发通知,收通知。这里面的注意点就是 ①对于系统没有定义的事件监听时需要自己发通知,这是你就需要定义一个key,字符串类型,这也是通知的一个弊端,你需要拷贝到收通知的对象,避免写错一个字母而无法收通知的尴尬 ②就是注册的通知中心需要手动移除,不然除了性能的问题还会有其他的问题出现,比如说一个控制器消失了之后还有因为某些事件而发出通知,造成不想要的结果。
第三就是block了,这是苹果后来才加入的,也是目前开发比较常用的一种方式,功能比较强大,但是在理解和使用上可能需要一段时间摸索和熟悉。他的最大特点就是回调,而且回调时可以传入参数,最重要的是,无论在哪调用,block的执行都会回到block创建的地方执行,而非调用的地方。而block本身可以封装一段代码,一段代码你懂的,很多人在初学时会被搞晕,甚至在block的声明上就纠结,其实很正常,多用就好。
此处还是简单说一下语法的东西 ①声明分为3部分 返回值类型 + 变量名 + 参数 比如 成员变量的声明 void (^myblock)(int a) &
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:7776次
排名:千里之外
原创:15篇
(1)(1)(10)(9)(1)关于IOS代理和block使用的不同
虽然block使用起来非常灵活,但代理也有其方便之处。
今天突然发现了这点。举个例子说明:block要传递该函数,首先函数中的对方必须是在函数体中声明或者是引用了外部的对象。这就有个问题,就是在写函数声明的时候,里面的对象都存在了,或者说了先有了对象,后面启动block的时候,才进行的回调。
代理对于解决这种不可预知的对象是很有帮助的,实际上我不需要管你是否存在,我就可以调用让你执行这个协议方法。这是因为代理的启动可以加判断,如:if(self.delegate
&&self.delegate
responseto:),加了这个判断之后,你对象有没有我也不关心,反正对象有,我就启动,没有就不启动。就是这么简单。这对于解决我不确定对象存在的情况下,是很有用处的。
如XMPP协议中,登录进去之后,XMPP首先就执行了didreceivePresent这个方法。执行过后,本来要更新在线联系人列表,这个列表可以更新,但是是否要刷新UI,就很难判断。因为UI的启动时间很难把握,可能在didreceivepresent之前或者之后都有可能。这个时候,可以通过代理,启动刷新相关UI,如果那个controller存在,就刷新,不存在,就不刷新,UI启动后会自己更新最新的在线联系人数据。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 buffer.blockcopy用法 的文章

 

随机推荐