1.使用Blocks
(1)block概念
Blocks是带有自动变了(局部变量)的匿名函数。其写法遵循BN范式
^ 返回值类型 参数列表 表达式
返回值类型可省略,参数列表亦可省略。可以定一个Blocks变量来获得匿名函数的使用权
如
int (^blk)(int) = ^(int count){return count+1};
我们可以作为参数传递Blocks变量,也可以返回Blocks类型变量,也可以使用typedef定义更方便使用Blocks变量
如
void func(int (^blk)(int)){}
int (^func())(int){
return ^(int count){return count+1};
}
typedef int (^blc_t)(int);
- block的代码是内联的,效率高于函数调用
- block对于外部变量默认是只读属性,要在block内修改外部变量需要添加_block关键字修饰
- block被Objective-C看成是对象处理
block会捕获在其定义时的变量值,变量修改后并不会影响block中使用的变量。
如
{
int a = 10;
int (^blc)(int) = int ^(int count){return count+a;};
a++;
NSLog(@"%@",blc(5));//打印15,而不是16
}
变量的复制关系如下
block外变量引用,默认是复制到block内的readonly变量
[站外图片上传中...(image-7c89d1-1524555790555)]
对于用__ block修饰的外部变量引用,block复制其引用地址来实现访问
[站外图片上传中...(image-a85bf0-1524555790555)]
(2)block常见用法:
-
局部位置声明一个Block型的变量
return_type (^blockName)(var_type) = ^return_type (var_type varName) { // ... }; blockName(var);
-
@interface声明Block型属性
@property(nonatomic, copy)return_type (^blockName) (var_type);
-
Block型作为形参
- (void)yourMethod:(return_type (^)(var_type))blockName;
-
内联用法,定义后立即调用,不常用
^return_type (var_type varName) { //... }(var);
-
递归调用
使用
__block
避免循环引用问题。__block return_type (^blockName)(var_type) = [^return_type (var_type varName) { if (returnCondition) { blockName = nil; return; } // ... // 【递归调用】 blockName(varName); } copy]; 【初次调用】 blockName(varValue);
-
作为返回值
- (return_type(^)(var_type))methodName { return ^return_type(var_type param) { // ... }; }
2.block原理
(1)数据结构定义
block的数据结构定义如下
[站外图片上传中...(image-894ad2-1524555790555)]
结构体定义如下
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
unsigned long int reserved;
unsigned long int size;
};
struct Block_layout {
void *isa;
volatile int flags; // contains ref count
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 *descriptor;
// imported variables
};
//Block-private.h
对于每个参数解释如下
- isa: 对象指针
- flags: 记录block的一些附加信息,包括引用计数
- reserved: 保留变量。
- invoke: 函数指针,指向block函数实现地址。
- descriptor:附加信息描述
//falgs bit位描述如下
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_NEEDS_FREE = (1 << 24), // runtime
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
BLOCK_IS_GLOBAL = (1 << 28), // compiler
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30) // compiler
};
从枚举变量的定义可以看出flags的bit位作用,第0位表示释放内存标志,1-23bit作为引用计数值,24-31略过。
在OC中有三种block
- _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
- _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁,ARC下不适用。
- _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。
下篇文章讲解不同block的实现方式