Block 那些事

一. Block入门

1.概念

Block是iOS4.0+ 和Mac OS X 10.6+ 引进的对C语言的扩展,用来实现匿名函数的特性
闭包是一个能够访问其他函数内部变量的函数.

2.基本的用法

1blockcong-ru-men-dao

每次写block都很绕,语法很麻烦。好吧,再此记录一下。

1.作为方法时

2.作为成员变量进行声明时

3.写在函数内部

3.内存的5个区

这里先普及一个基本的5个概念,内存的5个区。否则在具体讲堆栈的时候怕大家会不理解。

作用 名称
栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量的值等
堆区(heap) 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收
全局区(静态区)(static) 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放
文字常量区 常量字符串就是放在这里的。程序结束后由系统释放
程序代码区 存放函数体的二进制代码

4.使用场景

  1. 任务完成时回调处理
  2. 消息监听回调处理
  3. 错误回调处理
  4. 枚举回调
  5. 视图动画、变换
  6. 排序

二. Block原理

1.Block的三种类型

作用 名称
NSConcreteGlobalBlock 全局静态,不访问外部变量的时候就是NSConcreteGlobalBlock
NSConcreteStackBlock 保存在栈中,出花括号会被销毁
NSConcreteMallocBlock 保存在堆中的block,当引用计数为0的时候会被销毁

2.类型判断

如何判断在哪个区,我们分ARC和MRC两种情况

1.ARC下

2.MRC下

3.判断原则

1.ARC下判断原则
3blockcong-ru-men-dao

2.MRC下判断原则
4blockcong-ru-men-dao

3.ARC与MRC区别
ARC引用外部对象会自动copy,MRC则不会。所以MRC要将栈对象变成堆对象只要执行一次copy即可。

3.循环引用

1.造成循环引用的实例

此时_a在用clang编译后可以很明显看到有会self的身影,所以此时我们可以将其理解成self.a(便于记忆),此时Person持有block,block持有self,造成了循环引用

2. 解决方式

3.系统Block

1.经验谈之GCD/UIView的系统动画的Block中,为何可以写self?
因为self并没有对其进行持有,循环引用的原理是两个对象之间相互有持有关系,现在仅仅是GCD持有self,但是self并没有持有GCD,所以是没有问题的。(当GCD对象为其成员变量时才具有持有的关系)

4.__block原理

1.我们先看如下代码

2.用clang -re-write testBlock.c命令后

看关键代码如下:

3.结论

我们观察Block_byref_a_0结构体,这个结构体中含有isa指针,所以也是一个对象,它是用来包装局部变量a的。当block被copy到堆中时,Persontest_block_impl_0的拷贝辅助函数Persontest_block_copy_0会将Block_byref_a_0拷贝至堆中,所以即使局部变量所在堆被销毁,block依然能对堆中的局部变量进行操作。其中Block_byref_a_0成员指针forwarding用来指向它在堆中的拷贝

三. 利用Block如何实现一对多的

1.一种是直接Block

这里我直接copy一段AFNetworking的代码,这个库写的太好了,大家有空可以参考一下

2.利用对象做Block

"Block语法"

现在有RequestObjectXXNetEngine两个文件.

1.RequestObject

2.XXNetEngine

3.输出结果

四. Delegate、Notification、Block

1.优缺点

名称 优点 缺点
Notification 1.使用简单,代码精简。2.解决了同时向多个对象监听相应的问题。3.传值方便快捷,Context自身携带相应的内容。 1.使用完毕后,要时刻记得注销通知,否则将出现不可预见的crash。2.key不够安全,编译器不会监测是否被通知中心正确处理。3.调试的时候动作的跟踪将很难进行。4.当使用者向通知中心发送通知的时候,并不能获得任何反馈信息。5.需要一个第三方的对象来做监听者与被监听者的中介。
Delegate 1.减少代码的耦合性,使事件监听和事件处理相分离。2.清晰的语法定义,减少维护成本,较强的代码可读性。3.不需要创建第三方来监听事件和传输数据。 1.实现委托的代码过程比较繁琐。2.当实现跨层传值监听的时候将加大代码的耦合性,并且程序的层次结构将变的混乱。3.当对多个对象同时传值响应的时候,委托的易用性将大大降低。
Block 1.语法简洁,实现回调不需要显示的调用方法,代码更为紧凑。2.增强代码的可读性和可维护性。3.配合GCD优秀的解决多线程问题。 1.Block中得代码将自动进行一次retain操作,容易造成内存泄露2.Block内默认引用为强引用,容易造成循环引用。

2.个人见解

如果方法过多,一般大于3个的时候,用delegate,因为此时用block,代码将很难维护。比如UITableViewDelegate等等UI组建,都是用的delegate。其他方式用block。
Notification能不用的时候尽量不用,缺点太多明显,增加调试难度。在工程大的情况下,极其难以维护。当然有些情况下也是必不可少的,比如观察者模式.

五. 测试

1.测试一

快来看看大家现在对Block的理解吧
Block测试题, 从唐巧大神博客看到的转载链接

2.测试二

现在ViewController中有一个XXEngine对象

1.XXEngine代码如下

2.我们在ViewController中做如下处理(1)

此时的输出结果大家能想到吗?这里公布答案:

3.我们在ViewController中做如下处理(2)

此时的输出结果大家能想到吗?这里公布答案:

4.结论

在(1)中,success是一个NSGlobalBlock对象,dispatch对success做了持有操作,GCD我们可以认为是一个系统单例对象,此时XXEngine虽然被释放了,但是success被GCD持有了一份,也就是引用技术+1,所以success这个block不会被释放。GCD持有的对象会被拷贝到堆中,现在GCD执行完成,堆中对象要进行释放,所以知道success完成后,GCD释放。Block可以回调的。

在(2)中,GCD对XXEngine进行了持有,也就是引用技术+1,此时ViewController执行完对XXEngine引用技术-1,但是还是无法释放XXEngine,等到GCD结束之后,对XXEngine引用技术-1,此时会进行XXEngine的dealloc

六. Demo下载

testBlock

七. 参考资料

1.Delegate,Notification,Block
2.AFNetworking 3.0迁移指南
3.Block技巧与底层解析
4.谈Objective-C Block的实现–>

1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部