初探iOS闭包实现Block

block当初是为了解决什么样的问题而设计的?你如何区分何时使用block,何时不使用?–casatwy

解决什么问题?为了解决不同对象间除了可以传递值还可以传递一个操作而设计的。
何时使用? 适用于回调
何时不使用?

20170216 – 还没有完全清晰的认识,欢迎交流.


声明

下图来自苹果官方文档。
block格式
示例大多是从AFNetworking中节取的,也可以参考这里How Do I Declare a Block:

// 1.typedef
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
// 2.声明为变量
int (^myBlock)(int) = ^(int num){ return num * 2; };
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { ... };
// 3.声明为属性
@property (nonatomic, copy) void (^blockName)(NSString *text);
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;// 通过typedef给block取别名
// 4.作为函数的参数
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler;
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block;
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;
// 5.调用具有block的函数
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ... }];
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ ... }];

意义

Block是苹果从iOS4.0开始引入的对C语言的扩展,用来实现闭包(匿名函数)的特性。闭包的概念起源于函数式语言,可以理解为是能够读取外部变量(静态变量或者函数内部变量)的函数。那么block是为了解决什么问题而设计的?
目前还没有答案。只能说下目前对此的理解:

Block VS Delegate

Block

block都是一些简短代码片段的封装,适用作工作单元,通常用来做并发任务、遍历、以及回调。 – Jason

优点

Blocks are particularly useful as a callback because the block carries both the code to be executed on callback and the data needed during that execution.–AppleDoc

  1. 支持直接访问词法范围内(lexical scope)的状态(全局变量,全局函数与block同一范围内的本地变量),延迟执行的代码也可以直接写在调用的block内。–>直接访问上下文,不需要存储临时变量
  2. 代码集中,简洁直观。–> 发起和响应部分的代码放在一起

    Can share the potential for modification with other blocks defined within the same lexical scope;
    Can continue to share and modify state defined within the lexical scope (the stack frame) after the lexical scope (the stack frame) has been destroyed – AppleDoc

  3. 相同词法范围内声明的block之间可以共享修改的状态(局部变量).并且词法范围内状态已经被销毁后仍可以继续共享被修改过的同词法范围内的状态。(ps: 这个还没有完全意会,欢迎交流)

缺点

block很难追踪,难以维护;block会延长相关对象的生命周期;block在离散型场景下不符合使用的规范,所以平时尽量不要滥用block,尤其是在网络层这里 —田伟宇

  1. 易造成循环引用,难以追踪维护;
  2. 会延长相关对象的生命周期;
  3. 占用内存,性能不如Delegate;

Delegate

优点
  1. 易于维护调试,不会出现循环引用的问题;
  2. 性能高;
缺点
  1. 声明与实现分离,代码连贯性不好,代码分散冗杂;
  2. 很多时候需要存储临时数据;

两者的适用场景

Block灵活但难以维护。Delegate限制了灵活性,但也换来了应用的可维护性。两者没有说哪一者更好,都有相应的使用场景。

Block适用的场景:
  1. 如果是回调或某些操作完成后运行的情况下,可以考虑使用block;
  2. 调用同一个函数,但是希望回调的内容不一样的情况下,适合block。 –> 网络请求,回调结果分为成功与失败。
  3. 如果操作间可以有同步或一步的顺序,使用block。
代理适用的场景:
  1. Block中引用对象会等到执行完毕才回收引用对象但也导致有的场景需要及时回收却无法及时回收,而代理则能及时回收;
  2. 多个相关方法,多于3个优先采用代理;
  3. 避免循环引用。

__block 变量

block内无法对变量进行写操作,但由__block修饰的变量可进行写操作。本质上也就是非__block修饰的是进行传值操作,__block修饰的是进行传地址操作。

block的存储类型

OC中,存储类型有3种:
1. _NSConcretGlobalBlock(全局的静态block,不会访问任何外部变量):当block定义在函数体外面,或者定义了一下而不使用函数体的内容。
2. _NSConcretStackBlock(保存在栈中,函数返回时会被销毁):当block在函数内部,且定义的时候就使用了函数内部的变量,函数返回时会被销毁。
3. _NSConcretMallocBlock(保存在堆中,引用计数为0时会被销毁):全局block存储在堆中,对全局block使用copy操作会返回原函数指针;而对栈中的block使用copy操作,会产生两个不同的block地址,也就是两个匿名函数的入口地址。
ps: ARC模式下,将保存在栈中的block被保存在堆中的block替代,因而存储类型为两种。唐巧在《iOS开发进阶中》中认为这么做的原因是将堆栈中的block全部放到堆上管理,对编译器来说比较方便。

内存类型

栈区(stack):由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。
堆区(heap):一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。
全局区/静态区:顾名思义,全局变量和静态变量存储在这个区域。只不过初始化的全局变量和静态变量存储在一块,未初始化的全局变量和静态变量存储在一块。程序结束后由系统释放。

小结

  1. 余书懿在博客中iOS 使用Block分析“使用Blcok的过程中,要注意避免循环引用。通常是将引用的变量定义成一个弱引用__weak。所以当引用的变量被释放时weakSelf就为nil。考虑到多线程情况,则需强引用weakSelf为strongSelf,使用过程中,引用变量不为nil就会retain self,防止后面使用过程中self已释放。 “
    bang对这个的理解” weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。”

    __weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;
    
        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }
    
    };

参考资料

苹果官方文档
iOS中block实现的探究–Jason
iOS开发基础:开发两年的你也不会写的Block

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值