iOS

Objective-C之Blocks

2017-02-19  本文已影响8人  阿咿呀嘿

一、概念

Block是带有自动变量值的匿名函数。相当于其他语言里闭包Closure、Lambda的概念。

二、语法

范式

返回值类型 (^Block变量名) 参数列表 = ^返回值类型 参数列表 表达式
其中,返回值类型可省略;参数个数为0时,参数列表可省略。如下示例:

  int (^add)(int a, int b) = ^int (int a, int b) {return a + b;};
  int (^sum)(int, int) = ^int (int a, int b) {return a + b;};
  int (^cc)(void) = ^int (void) { return 0; };
  int (^dd)() = ^int () { return 0; };
  int (^ee)() = ^{return 0;};
  void (^ff)() = ^ { NSLog(@"ff");};

Block类型书写比较复杂,可以使用typedef进行类型定义

  typedef int (^TestBlock)(int c);
  TestBlock (^hh)(int a, int b) = ^TestBlock (int a, int b) {return ^int (int c) {return 123;};};

Block的执行方式:

  int aaa =hh(1,2)(3);
  int s = add(1, 2);
  ^{NSLog(@"abc");}();

三、__block修饰符

四、Block的实质

main.m文件源代码:

int main()
{
    void (^blk)(void) = ^{};
    blk();
}

使用clang(LLVM编译器)将OC代码转换为C++代码,转换命令:clang -rewrite-objc main.m,转换结果main.cpp代码如下:

//Block类,相当于类的结构体
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

//Block对象,相当于类的对象的结构体

struct __main_block_impl_0 {
  //struct __block_impl impl;展开等价于
  //void *isa;
  //int Flags;
  //int Reserved;
  //void *FuncPtr;
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  //结构体构造函数
  //参数fp为C函数指针
  //参数desc为静态全局结构体__main_block_desc_0的实例指针
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
      //&_NSConcreteStackBlock相当于Block类的结构体指针,实现Block对象的isa指向Block类
      impl.isa = &_NSConcreteStackBlock;
      impl.Flags = flags;
      impl.FuncPtr = fp;
      Desc = desc;
  }
};

//OC语言Block块^{};对应的匿名函数
//通过Block使用的匿名函数被转换为简单的C函数,函数命名规则:__Block所在函数的名称_block_func_Block在函数中序号
//参数__cself为指向Block值的变量,相当于self和this的概念

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}

//Block描述信息结构体定义与其静态全局结构体实例__main_block_desc_0_DATA定义

static struct __main_block_desc_0 {
  size_t reserved;//保留字段
  size_t Block_size;//__main_block_impl_0结构体实例的大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main()
{
  //对应的OC代码:void (^blk)(void) = ^{};
  //调用结构体__main_block_impl_0的构造函数创建结构体实例,并赋值给结构体指针blk
  //下面为等价代码:
  //struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
  //struct __main_block_impl_0 *blk = tmp;
  void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

  //对应的OC代码:blk();
  //调用Block,代码实质:(blk->FuncPtr)(blk);
  ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}

可见,Block的本质也是对象。

五、捕获变量

Block捕获的变量可以分为两类:

C语言自动变量

Block自动将Block内部使用的自动变量作为结构体成员追加到Block结构数据中。由于初始化Block结构体实例时,直接将自动变量的赋值给对应结构成员,属于传值方式,故Block内无法修改外部自动变量。

全局变量、静态全局变量

Block内部可以直接访问全局变量和静态全局变量,也可以进行修改,Block结构体中不会自动追加相应成员变量。

静态局部变量

Block内访问静态局部变量时,Block结构体中会自动追加对应指针类型的成员,属于传地址方式,增加的结构体成员为指针类型,指向静态局部变量,故Block内可以修改静态局部变量的值。

OC对象型自动变量

Block内访问OC对象时,Block结构体会自动追加一个对象所有权类型相同的OC对象成员,该成员会持有外部的OC对象,故在Block内可以调用该OC对象的方法,或向其发送消息。

__block修饰的C语言自动变量

对于使用__block修饰的自动变量,编译器会自动生成一个特定结构体,结构体中包含一个保存实际数据的成员字段,以及一个指向自身类型的指针,Block结构体会追加一个指向该特定结构体的实例的指针成员。

__block修饰的OC对象型变量

对于使用__block修饰的自动变量,编译器会自动生成一个特定结构体,结构体中包含一个保存实际数据的成员字段(对象所有权修饰符也保持一致),以及一个指向自身类型的指针、用于copy对象的函数指针,用于释放对象的函数指针,Block结构体会追加一个指向该特定结构体的实例的指针成员。

示例代码main.m文件源代码如下:
#import <Foundation/Foundation.h>
int global_val = 1;//全局变量
static int static_global_val = 2;//静态全局变量
void (^global_Block)(void) = ^{global_val += 1;};//全局Block
int main()
{
static int static_val = 3;//静态局部变量
int val1 = 10;//自动变量
const char fmt = "val = %d\n";//指针自动变量
__strong id obj1 = [[NSObject alloc] init];//强引用OC对象自动变量
__unsafe_unretained id obj2 = obj1;//__unsafe_unretained类型OC对象自动变量
__weak id obj3 = obj1;//弱引用OC对象自动变量
__block __strong id obj4 = obj1;//__block型强引用OC对象自动变量
__block __weak id obj5 = obj1;//__block型弱引用OC对象自动变量
__block int val2 = 10;//__block型C语言自动变量
//局部Block
void (^blk)(void) = ^{
global_val += 1;
static_global_val += 1;
static_val += 1;
val1;
fmt;
obj1;
obj2;
val2;
obj3;
obj4;
obj5;
};
blk();//执行Block
void (^blk2)(void) = ^{/
不截获任何变量的Block*/};
void (^blk_copy)(void) = [blk copy];//复制到堆上的Block
return 0;
}
使用clang(LLVM编译器)将OC代码转换为C++代码,转换命令:clang -fobjc-arc -framework Foundation -rewrite-objc -fobjc-runtime=macosx-10.11 main.m,转换结果main.cpp代码如下:

int global_val = 1;
static int static_global_val = 2;

//全局Block对应的结构体
struct __global_Block_block_impl_0 {
    struct __block_impl impl;
    struct __global_Block_block_desc_0* Desc;
    __global_Block_block_impl_0(void *fp, struct __global_Block_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteGlobalBlock;//全局Block位于程序静态数据区
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
//global_Block全局Block对应的匿名函数
static void __global_Block_block_func_0(struct __global_Block_block_impl_0 *__cself) {
global_val += 1;}
//全局Block对应的描述信息结构体
static struct __global_Block_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __global_Block_block_desc_0_DATA = { 0, sizeof(struct __global_Block_block_impl_0)};
//创建全局Block对象,并赋值给该Block类型变量global_Block
static __global_Block_block_impl_0 __global_global_Block_block_impl_0((void *)__global_Block_block_func_0, &__global_Block_block_desc_0_DATA);
void (*global_Block)(void) = ((void (*)())&__global_global_Block_block_impl_0);

//只要声明了__block类型的变量,转换的C++代码中自动为其创建的结构体
//强引用型OC对象__block变量obj4对应的结构体
struct __Block_byref_obj4_0 {
    void *__isa;
    __Block_byref_obj4_0 *__forwarding;//指向结构体自身,或栈上的__block变量被复制到堆上后,栈上的__block变量的该指针指向堆上的__block变量
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);//copy函数的指针
void (*__Block_byref_id_object_dispose)(void*);//释放函数的指针
__strong id obj4;//实际封装的数据,所有权类型保持一致
};
//弱引用型OC对象__block变量obj5对应的结构体
struct __Block_byref_obj5_1 {
    void *__isa;
    __Block_byref_obj5_1 *__forwarding;
    int __flags;
    int __size;
    void (*__Block_byref_id_object_copy)(void*, void*);
    void (*__Block_byref_id_object_dispose)(void*);
    __weak id obj5;
};
//C语言__block自动变量val2对应的结构体
struct __Block_byref_val2_2 {
    void *__isa;
    __Block_byref_val2_2 *__forwarding;
    int __flags;
    int __size;
    int val2;
};
//blk对应的Block结构体
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int *static_val;//对应静态自动变量static_val
    int val1;//对应自动变量val1
    const char *fmt;//对应自动变量fmt
    __strong id obj1;//对应OC自动变量obj1
    __unsafe_unretained id obj2;//对应OC自动变量obj2
    __weak id obj3;//对应OC自动变量obj3
    __Block_byref_val2_2 *val2; //对应__block自动变量val2
    __Block_byref_obj4_0 *obj4; //对应__block自动变量obj4
    __Block_byref_obj5_1 *obj5; //对应__block自动变量obj5
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int _val1, const char *_fmt, __strong id _obj1, __unsafe_unretained id _obj2, __weak id _obj3, __Block_byref_val2_2 *_val2, __Block_byref_obj4_0 *_obj4, __Block_byref_obj5_1 *_obj5, int flags=0) : static_val(_static_val), val1(_val1), fmt(_fmt), obj1(_obj1), obj2(_obj2), obj3(_obj3), val2(_val2->__forwarding), obj4(_obj4->__forwarding), obj5(_obj5->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;//位于栈上
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
//栈上Block对应的匿名函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    __Block_byref_val2_2 *val2 = __cself->val2; // bound by ref
    __Block_byref_obj4_0 *obj4 = __cself->obj4; // bound by ref
    __Block_byref_obj5_1 *obj5 = __cself->obj5; // bound by ref
    int *static_val = __cself->static_val; // bound by copy
    int val1 = __cself->val1; // bound by copy
    const char *fmt = __cself->fmt; // bound by copy
    __strong id obj1 = __cself->obj1; // bound by copy
    __unsafe_unretained id obj2 = __cself->obj2; // bound by copy
    __weak id obj3 = __cself->obj3; // bound by copy

    global_val += 1;
    static_global_val += 1;
    (*static_val) += 1;
    val1;
    fmt;
    obj1;
    obj2;
    (val2->__forwarding->val2);
    obj3;
    (obj4->__forwarding->obj4);
    (obj5->__forwarding->obj5);
}

//copy函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj1, (void*)src->obj1, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->obj2, (void*)src->obj2, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->val2, (void*)src->val2, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj3, (void*)src->obj3, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->obj4, (void*)src->obj4, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj5, (void*)src->obj5, 8/*BLOCK_FIELD_IS_BYREF*/);}

//释放函数
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->obj1, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->obj2, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->val2, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj3, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->obj4, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj5, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

//blk2对应的Block结构体
struct __main_block_impl_1 {
    struct __block_impl impl;
    struct __main_block_desc_1* Desc;
    __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;//位于栈上
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __main_block_func_1(struct __main_block_impl_1 *__cself) {
}

static struct __main_block_desc_1 {
    size_t reserved;
    size_t Block_size;
} __main_block_desc_1_DATA = { 0, sizeof(struct __main_block_impl_1)};
int main()
{
    static int static_val = 3;
    int val1 = 10;
    const char *fmt = "val = %d\n";
    __attribute__((objc_ownership(strong))) id obj1 = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    __attribute__((objc_ownership(none))) id obj2 = obj1;
    __attribute__((objc_ownership(weak))) id obj3 = obj1;
    __attribute__((__blocks__(byref))) __attribute__((objc_ownership(strong))) __Block_byref_obj4_0 obj4 = {(void*)0,(__Block_byref_obj4_0 *)&obj4, 33554432, sizeof(__Block_byref_obj4_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, obj1};
    __attribute__((__blocks__(byref))) __attribute__((objc_ownership(weak))) __Block_byref_obj5_1 obj5 = {(void*)0,(__Block_byref_obj5_1 *)&obj5, 33554432, sizeof(__Block_byref_obj5_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, obj1};
    __attribute__((__blocks__(byref))) __Block_byref_val2_2 val2 = {(void*)0,(__Block_byref_val2_2 *)&val2, 0, sizeof(__Block_byref_val2_2), 10};
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val, val1, fmt, obj1, obj2, obj3, (__Block_byref_val2_2 *)&val2, (__Block_byref_obj4_0 *)&obj4, (__Block_byref_obj5_1 *)&obj5, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    void (*blk2)(void) = ((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA));
    void (*blk_copy)(void) = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)blk, sel_registerName("copy"));
    return 0;
}

通常C语言结构体中不能包含OC对象型成员,因为编译器不能很好地对C语言里OC对象进行内存管理,但是,Block结构体中可以包含OC对象,因为编译自动生成了copy和dispose函数实现对OC对象成员的内存管。copy函数在Block从栈上复制到堆上时被调用,dispose函数在堆上的Block被释放回收时被调用。

六、存储域

Block的实质:Block的结构体实例自动变量,Block也是OC对象。__block变量的实质:栈上__block变量的结构体实例自动变量。根据Block存储域,Block对应的类有三种:

Block从栈复制到堆上时,栈上的__block变量被复制到堆上并被堆上Block持有,栈上被Block捕获的OC对象自动变量也会被堆上Block持有。所以,copy后,栈上的OC对象自动变量和__block变量可以超过作用域而存在。Block从栈上被复制到堆上本质都是调用copy方法,有四种情况:

七、Block循环引用

如果在Block中使用了附有__strong修饰符的对象类型自动变量,那么当Block从栈复制到堆上时,该对象会被堆上Block所持有,如果堆上的Block又由该对象所直接或间接持有,这样就导致循环引用。避免循环引用的三种方式:

上一篇 下一篇

猜你喜欢

热点阅读