《Objective-C 高级编程》读书笔记之 Blocks

前言

本文作为《Objective-C 高级编程》读书笔记的第二篇,给大家带来的是关于 Blocks 的知识点总结。


概念

Blocks 是 C 语言的扩充功能,可以用一句话来表示 Blocks 的扩充功能:带有自动变量(局部变量)的匿名函数。(Blocks 是闭包在 OC 语言中的实现,并不是 iOS 独有的概念,在 C++、Java 等语言也有实现闭包,只是名称不同而已)


优势

  1. 可代替 Delegate 完成回调,而不需要像 Delegate 那样繁琐
  2. 在某些方面,可代替 selector(如 NSNotificationCenter 在 addObserver 的时候,可以使用 block,而不用单独定义方法)
  3. 延长对象的生命周期(Block 会自动持有对象)
  4. 提高代码的复用性和可读性
  5. 常用于:View 动画、GCD、网络异步请求

语法

关于 Blocks 的语法,看下面一张图就可以啦:


Blocks 的实现

Blocks 的数据结构

对应的结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 部分构成:

  1. isa 指针: 所有对象都有该指针,用于实现对象相关的功能

  2. flags: 用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码可以看到对该变量的使用;

  3. reserved: 保留变量;

  4. invoke: 函数指针,指向具体的 block 实现的函数调用地址;

  5. descriptor: 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针;

  6. 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 修饰符来避免循环引用。


要点

  1. Block 执行的代码其实在编译的时候就已经准备好了

  2. 本身 Block 就是一个普通的 OC 对象。正因为它是对象,Block 可以被作为参数传递,可以作为返回值从一个方法返回,可以用来给变量赋值

  3. __block 修饰符在 MRC 下不会进行引用计数加 1,而 ARC 下则会加 1

  4. 对于 Block 外的变量引用,Block 默认是将其复制到其数据结构中来实现访问的

  5. 对于用 __block 修饰的外部变量引用,Block 是复制其引用地址来实现访问的

原文:


参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
51232是一种Raspberry Pi开发板型号,它可以与Simulink驱动程序块一起使用。Simulink是一种用于模拟、建模和代码生成的工具,而Raspberry Pi是一种可以运行Simulink模型的单板计算机。 Simulink驱动程序块是用于将Raspberry Pi的功能与Simulink模型集成的工具。它允许用户通过Simulink模型来控制和读取Raspberry Pi上的各种传感器和执行器。用户可以通过简单地将各种驱动程序块添加到Simulink模型中来实现与Raspberry Pi的通信和控制。 ADC表示模拟转数字转换器,DAC表示数字转模拟转换器,PWM表示脉冲宽度调制。Raspberry Pi上的ADC和DAC驱动程序块可以实现数据的模拟输入和输出。用户可以将传感器的模拟信号通过ADC转换为数字信号,并在Simulink模型中进行处理。类似地,用户可以使用DAC将模拟输出信号从Simulink模型发送到执行器。 PWM驱动程序块可以通过产生脉冲宽度调制信号来控制Raspberry Pi上的PWM输出引脚。PWM信号可以用于控制各种执行器,如电机和舵机。用户可以在Simulink模型中调整PWM信号的参数,并通过Raspberry Pi的PWM引脚将其输出。 总而言之,51232 Raspberry Pi Simulink驱动程序块可以实现与Simulink模型的综合,通过ADC和DAC驱动程序块进行模拟输入和输出,通过PWM驱动程序块控制PWM输出。这使得用户能够利用Simulink的功能来控制和监测Raspberry Pi上的各种设备和传感器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值