《Objective-C 高级编程》读书笔记之 Blocks
来源:互联网 发布:35互联域名管理平台 编辑:程序博客网 时间:2024/06/08 10:54
前言
本文作为《Objective-C 高级编程》读书笔记的第二篇,给大家带来的是关于 Blocks 的知识点总结。
概念
Blocks 是 C 语言的扩充功能,可以用一句话来表示 Blocks 的扩充功能:带有自动变量(局部变量)的匿名函数。(Blocks 是闭包在 OC 语言中的实现,并不是 iOS 独有的概念,在 C++、Java 等语言也有实现闭包,只是名称不同而已)
优势
- 可代替 Delegate 完成回调,而不需要像 Delegate 那样繁琐
- 在某些方面,可代替 selector(如 NSNotificationCenter 在 addObserver 的时候,可以使用 block,而不用单独定义方法)
- 延长对象的生命周期(Block 会自动持有对象)
- 提高代码的复用性和可读性
- 常用于:View 动画、GCD、网络异步请求
语法
关于 Blocks 的语法,看下面一张图就可以啦:
Blocks 的实现
Blocks 的数据结构
对应的结构体定义如下:
123456789101112131415
struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *);};struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */};
通过该图,我们可以知道,一个 Block 实例实际上由 6 部分构成:
isa 指针: 所有对象都有该指针,用于实现对象相关的功能
flags: 用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码可以看到对该变量的使用;
reserved: 保留变量;
invoke: 函数指针,指向具体的 block 实现的函数调用地址;
descriptor: 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针;
variables: capture 过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中;
Block 存储域
Objective-C 中的 Stack 和 Heap
首先所有的 Objective-C 对象都是分配在 Heap 的。 在 OC 最典型的内存分配与初始化就是这样的:
NSObject *obj = [[NSObject alloc] init];
一个对象在 alloc 的时候,就在 Heap 分配了内存空间。
Stack 对象通常有速度的优势,而且不会发生内存泄露问题。那么为什么 OC 的对象都是分配在 Heap 的呢? 原因在于:
Stack 对象的生命周期所导致的问题。例如一旦函数返回,则所在的 Stack Frame(栈帧)就会被销毁。那么此时返回的对象也会一并销毁。这个时候我们去 retain 这个对象是无效的。因为整个 Stack Frame 都已经被销毁了。简单而言,就是 Stack 对象的生命周期不适合 OC 的引用计数内存管理方法。
Stack 对象不够灵活,不具备足够的扩展性。创建时长度已经是固定的,而stack对象的拥有者也就是所在的 Stack Frame
Block 类型
应用程序的内存分配:
在 OC 中,一共有 3 种类型的 Block:
_NSConcreteGlobalBlock
_NSConcreteGlobalBlock:全局的静态 Block,不会访问任何外部变量。
_NSConcreteStackBlock
_NSConcreteStackBlock:保存在栈中的 Block,当函数返回时会被销毁。(ARC 中系统实现了自动 copy, 将创建在栈上的 Block 自动拷贝到堆上,所以不存在此类型的 Block)
_NSConcreteMallocBlock
_NSConcreteMallocBlock:保存在堆中的 Block,当引用计数为 0 时会被销毁。(即成为正常的 OC 对象)
Block 循环引用
如果在 Block 中使用附有 __strong 修饰符的对象类型自动变量,那么当 Block 从栈复制到堆时,该对象为 Block 所持有,于是便导致了循环引用的产生。
如图所示:self 持有 Block,Block 持有 self,这正是循环引用。
MRC
在 MRC 下,使用 __block 说明符来避免 Block 中的循环引用。
这是由于当 Block 从栈复制到堆时,若 Block 使用的变量为附有 block 说明符的 id 类型或对象类型的自动变量,不会被 retain;若 Block 使用的变量为没有block 说明符的 id 类型或对象类型的自动变量,则被 retain;若 Block 使用的变量为没有 __block 说明符的 id 类型或对象类型的自动变量,则被 retain。
ARC
在 ARC 下,为了避免这种情况发生,可以在变量声明时用 weak 修饰符修饰变量 self,让 block 不强引用 self,从而破除循环。iOS4 和 Snow Leopard 由于对 weak 的支持不够完全,可以用unsafe_unretained 代替。
使用 Block 成员变量避免循环引用:
比较
下面对使用 block 变量避免循环引用的方法和使用 weak 修饰符及 __unsafe_unretained 修饰符避免循环引用的方法做个比较。
使用 __block 变量的优点如下:
- 通过
__block
变量可控制对象的持有期间 在不能使用
__weak
修饰符的环境中不使用__unsafe_unretained
修饰符即可(不必担心悬垂指针)在执行 Block 时可动态地决定是否将 nil 或其他对象赋值在
__block
变量中。
使用 __block 变量的缺点如下:
为避免循环引用必须执行 Block
存在执行了 Block 语法,却不执行 Block 的路径时,无法避免循环引用。若由于 Block 引发了循环引用时,根据 Block 的用途选择使用
__block
变量、__weak
修饰符或__unsafe_unretained
修饰符来避免循环引用。
要点
Block 执行的代码其实在编译的时候就已经准备好了
本身 Block 就是一个普通的 OC 对象。正因为它是对象,Block 可以被作为参数传递,可以作为返回值从一个方法返回,可以用来给变量赋值
__block 修饰符在 MRC 下不会进行引用计数加 1,而 ARC 下则会加 1
对于 Block 外的变量引用,Block 默认是将其复制到其数据结构中来实现访问的
对于用 __block 修饰的外部变量引用,Block 是复制其引用地址来实现访问的
原文:
参考
- Working with Blocks
- How Do I Declare A Block in Objective-C?
- 谈Objective-C block的实现
- Objective-C 拾遗:从Heap and Stack到Block
- block没那么难(一):block的实现
- 正确使用Block避免Cycle Retain和Crash
- 《Objective-C 高级编程》读书笔记之 Blocks
- 《Objective-C 高级编程》读书笔记之 ARC
- 《Objective-C 高级编程》读书笔记之 GCD
- 《Objective-C 高级编程》读书笔记之 ARC
- Objective-C高级编程读书笔记之GCD
- <iOS读书笔记>之Objective-C高级编程(GCD)
- 《Objective-C 高级编程》干货三部曲(二):Blocks篇
- 【读书笔记】Objective-C高级编程--GCD概要
- 【读书笔记】Objective-C高级编程--Block概要
- 【读书笔记】Objective-C高级编程--GCD的API
- 【读书笔记】Objective-C高级编程--GCD实现1
- 【读书笔记】Objective-C高级编程--Block模式1
- 《objective-c基础编程》读书笔记
- 【Objective-C高级编程】iOS与OS X多线程和内存管理(五) Blocks 截获自动变量值
- 《Objective-C高级编程 iOS与OS X多线程和内存管理》学习笔记——第二章Blocks
- Objective-C Blocks
- Objective-C Blocks学习
- Objective-C Blocks学习
- Apache Tiles的基本使用
- HTML5教程-CSS基础-清除浮动-李南江
- 关于ORACLE游标的问题(ORA-01000: maximum open cursors exceeded)
- [已解决]android的textview 里面的text 怎么也不到中间的位置,这个时候需要设置android:includeFontPadding="false"
- ActiveMQ应用详解
- 《Objective-C 高级编程》读书笔记之 Blocks
- 网易视频云:分布式视频处理集群中的作业调度
- APP安全之代码混淆防止反编译查看真实的头文件
- 十分钟解决爬虫问题!超轻量级反爬虫方案
- 哈尔滨理工大学软件学院OJ 1419: 最大的XⅡ
- python安装包
- Mac 用命令行终端打开app应用程序
- 《Kubernetes与云原生应用》系列之四——实践案例“单节点多容器模式”
- JavaScript工具库之Lodash