深入 Block

Block 前言

BlockOC中对C语言的扩展功能,是一种带有自动变量的匿名函数,BlockOC中的实现,点击前往源码页面

/* Revised new layout. */
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_layout很容易看到isa,可知OC处理block是按照对象来处理的。isa常见的就是_NSConcreteStackBlock_NSConcreteMallocBlock_NSConcreteGlobalBlock
下面来研究一下Block的捕获外部变量的特性以及__block的实现原理

Block 怎么获取外部变量值

说到变量,一共有以下几种:

  • 自动变量
  • 函数参数(block捕获外部变量 要除去函数参数一项)
  • 静态变量
  • 静态全局变量
  • 全局变量

测试代码:

static int global_static_int = 1;
int global_int = 1;

int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
        static int static_int = 1;
        int var_int = 1;
        
        void (^block)(void) = ^{
            static_int ++;
            //var_int ++; // 报错:不可赋值(缺少__block类型说明符)
            global_int ++;
            global_static_int ++;
            NSLog(@"in block global_static_int = %d\n, global_int = %d \n,static_int = %d \n,var_int = %d",global_static_int,global_int,static_int,var_int);
        };
        
        static_int ++;
        var_int ++;
        global_int ++;
        global_static_int ++;
        NSLog(@"out block global_static_int = %d\n, global_int = %d \n,static_int = %d \n,var_int = %d",global_static_int,global_int,static_int,var_int);
        NSLog(@"**************** 分割线 ****************");
        
        block();
        
    }
    return 0;
}

运行结果

out block
global_static_int = 2
, global_int = 2 
,static_int = 2 
,var_int = 2
**************** 分割线 ****************
in block global_static_int = 3
, global_int = 3 
,static_int = 3 
,var_int = 1

由此得出以下结论:

  • 为什么在Block里面不加__block不允许改变变量
  • 为什么 var_int变量的值没有增加,其他几个会增加

为了研究实现原理,我们通过以下clang命令,将OC转化为C/C++语言来研究Block各个特性的实现方式

clang -rewrite-objc main.m

转换后的源码如下(clang转换后.cpp 文件后,代码有10w多行,取自身需要的源码:):

static int global_static_int = 1;
int global_int = 1;


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_int;
  int var_int;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_int, int _var_int, int flags=0) : static_int(_static_int), var_int(_var_int) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_int = __cself->static_int; // bound by copy
  int var_int = __cself->var_int; // bound by copy

            (*static_int) ++;

            global_int ++;
            global_static_int ++;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_0,global_static_int,global_int,(*static_int),var_int);
        }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        static int static_int = 1;
        int var_int = 1;

        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_int, var_int));


        static_int ++;
        var_int ++;
        global_int ++;
        global_static_int ++;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_1,global_static_int,global_int,static_int,var_int);

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_2);

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

首先全局变量global_int静态全局变量global_static_int的值增加,以及它们被Block捕获进去,这一点很好理解,因为是全局的,作用域很广,所以Block捕获了它们进去之后,在Block里面进行++操作,Block结束之后,它们的值依旧可以得以保存下来

__main_block_impl_0中,可以看到静态变量static_int和自动变量var_int,被Block从外面捕获进来,成为__main_block_impl_0这个结构体的成员变量,构造函数如下

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_int, int _var_int, int flags=0) : static_int(_static_int), var_int(_var_int) // 自动变量和静态变量被捕获称为成员变量追加到构造函数后

main里面的block中的__main_block_impl_0初始化如下:

void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val));


impl.isa = &_NSConcreteStackBlock;
impl.Flags = 0;
impl.FuncPtr = __main_block_impl_0; 
Desc = &__main_block_desc_0_DATA;
* _static_int = 1;
var_int = 1; 

到此,__main_block_impl_0结构体就是这样把自动变量捕获进来的。也就是说,在执行Block语法的时候,Block语法表达式所使用的自动变量的值是被保存进了Block的结构体实例中,也就是Block自身中,注:block使用不到的值,并不会去捕获
我们再来看__main_block_func_0这个函数:

__main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_int = __cself->static_int; // bound by copy
  int var_int = __cself->var_int; // bound by copy

            (*static_int) ++;

            global_int ++;
            global_static_int ++;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_0,global_static_int,global_int,(*static_int),var_int);
        }

bound by copy,自动变量val_int虽然被捕获进来了,但是是用 __cself->val来访问的。Block仅仅捕获了val的值,并没有捕获val_int的内存地址。所以在__main_block_func_0这个函数中即使我们重写这个自动变量val_int的值,依旧没法去改变Block外面自动变量val_int的值,这就是之前在block中使用var_int会报错的原因。

自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外表变量可以改变值的是静态变量,静态全局变量,全局变量。上面例子也都证明过了。

回到上面的例子,4种变量里面只有静态变量,静态全局变量,全局变量这3种是可以在Block里面被改变值的。仔细观看源码,我们能看出这3个变量可以改变值的原因。

  1. 静态全局变量,全局变量由于作用域的原因,于是可以直接在Block里面被改变。他们也都存储在全局区
  2. 静态变量传递给Block是内存地址值,所以能在Block里面直接改变值。

Block中改变变量值有2种方式,
一. 是传递内存地址指针到Block中,
二. 是改变存储区方式(__block)

Block的copy 与dispose

OC中,一般Block就分为以下3种,_NSConcreteStackBlock_NSConcreteMallocBlock_NSConcreteGlobalBlock。哪3者的区别呢。

  1. 从捕获变量来说
  • _NSConcreteStackBlock
    只用到外部局部变量,成员属性变量,且没有强指针引用的都是Stack Block,声明周期由系统控制,一旦返回就销毁
  • _NSConcreteMallocBlock
    有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制
  • _NSConcreteGlobalBlock
    没有用到外界变量或只用到全局变量、静态变量的block_NSConcreteGlobalBlock,生命周期从创建到应用程序结束
  1. 从持有对象的角度上来看
  • _NSConcreteStackBlock不持有对象
  • _NSConcreteMallocBlock持有对象的
  • _NSConcreteGlobalBlock 不持有对象

_NSConcreteStackBlock所属的变量域一旦结束,那么该Block就会被销毁。在ARC环境下,编译器会自动的判断,把Block自动的从栈copy到堆。比如当Block作为函数返回值的时候,肯定会copy到堆上

以下四种方法系统都会默认调用Blockcopy方法

  1. 手动调用copy
  2. Block是函数的返回值`
  3. Block被强引用,Block被赋值给__strong或者id类型
  4. 调用系统API入参中含有usingBlcok的方法
    copy函数把Block从栈上拷贝到堆上,dispose函数是把堆上的函数在废弃的时候销毁掉

以下源码中2个常用的宏定义和4个常用的方法

#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))

// Create a heap based copy of a Block or simply add a reference to an existing one.
// This must be paired with Block_release to recover memory, even when running
// under Objective-C Garbage Collection.
BLOCK_EXPORT void *_Block_copy(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

// Lose the reference, and if heap based and last reference, recover the memory
BLOCK_EXPORT void _Block_release(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

// Used by the compiler. Do not call this function yourself.
BLOCK_EXPORT void _Block_object_assign(void *, const void *, const int)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

// Used by the compiler. Do not call this function yourself.
BLOCK_EXPORT void _Block_object_dispose(const void *, const int)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

以下为Block_copy的一个实现,实现了从_NSConcreteStackBlock复制到_NSConcreteMallocBlock的过程。对应有9个步骤

static void *_Block_copy_internal(const void *arg, const int flags) {
    struct Block_layout *aBlock;
    const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
    
    // 1
    if (!arg) return NULL;
    
    // 2
    aBlock = (struct Block_layout *)arg;
    
    // 3
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    
    // 4
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }
    
    // 5
    struct Block_layout *result = malloc(aBlock->descriptor->size);
    if (!result) return (void *)0;
    
    // 6
    memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    
    // 7
    result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
    result->flags |= BLOCK_NEEDS_FREE | 1;
    
    // 8
    result->isa = _NSConcreteMallocBlock;
    
    // 9
    if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
        (*aBlock->descriptor->copy)(result, aBlock); // do fixup
    }
return result;
}

以下为Block_release的实现,实现了怎么释放一个Block。对应有6个步骤

void _Block_release(void *arg) {
    // 1
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    if (!aBlock) return;
    
    // 2
    int32_t newCount;
    newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK;
    
    // 3
    if (newCount > 0) return;
    
    // 4
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock);
        _Block_deallocator(aBlock);
    }
    
    // 5
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        ;
    }
    
    // 6
    else {
        printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock);
    }
}

当使用,字符串的例子中来,转换源码之后,我们会发现多了一个copy和dispose方法。
因为在C语言的结构体中,编译器没法很好的进行初始化和销毁操作。这样对内存管理来说是很不方便的。所以就在 __main_block_desc_0结构体中间增加成员变量 void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*)void (*dispose)(struct __main_block_impl_0*),利用OCRuntime进行内存管理。

相应的增加了2个方法。

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}

这里的_Block_object_assign_Block_object_dispose就对应着retainrelease方法。

BLOCK_FIELD_IS_OBJECTBlock截获对象时候的特殊标示,如果是截获的__block,那么是BLOCK_FIELD_IS_BYREF

Block中__block实现原理
  1. 普通非对象的变量
struct __Block_byref_var_int_0 {
  void *__isa;
__Block_byref_var_int_0 *__forwarding;
 int __flags;
 int __size;
 int var_int;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_var_int_0 *var_int; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_int_0 *_var_int, int flags=0) : var_int(_var_int->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_var_int_0 *var_int = __cself->var_int; // bound by ref

            (var_int->__forwarding->var_int) ++;


            NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_97bc98_mi_0, (var_int->__forwarding->var_int));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var_int, (void*)src->var_int, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var_int, 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};
int main(int argc, const char * argv[]) {

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_var_int_0 var_int = {(void*)0,(__Block_byref_var_int_0 *)&var_int, 0, sizeof(__Block_byref_var_int_0), 1};

        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_int_0 *)&var_int, 570425344));

        (var_int.__forwarding->var_int) ++;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_97bc98_mi_1, (var_int.__forwarding->var_int));

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_97bc98_mi_2);

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

从源码我们能发现,带有__block的变量也被转化成了一个结构体__Block_byref_var_int_0,这个结构体有5个成员变量。第一个是isa指针,第二个是指向自身类型的__forwarding指针,第三个是一个标记flag,第四个是它的大小,第五个是变量值,名字和变量名同名

__attribute__((__blocks__(byref))) __Block_byref_var_int_0 var_int = {(void*)0,(__Block_byref_var_int_0 *)&var_int, 0, sizeof(__Block_byref_var_int_0), 1};

源码中是这样初始化的。__forwarding指针初始化传递的是自己的地址。然而这里__forwarding指针真的永远指向自己么?

我们把Block拷贝到了堆上,这个时候打印出来的2个i变量的地址就不同了

int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
        __block int var_int = 1;
        
        NSLog(@"%p",&var_int);
        
        void (^block)(void) = [^{
            var_int ++;
            

            NSLog(@"%p",&var_int);
        } copy];
        
        
        NSLog(@"**************** 分割线 ****************");
        
        block();
        
    }
    return 0;
}

地址不同就可以很明显的说明__forwarding指针并没有指向之前的自己了。那__forwarding指针现在指向到哪里了呢?__block现在应该也在堆上。

前面,堆上的Block会持有对象。我们把Block通过copy到了堆上,堆上也会重新复制一份Block,并且该Block也会继续持有该__block。当Block释放的时候,__block没有被任何对象引用,也会被释放销毁。

__forwarding指针这里的作用就是针对堆的Block,把原来__forwarding指针指向自己,换成指向_NSConcreteMallocBlock上复制之后的__block自己。然后堆上的变量的__forwarding再指向自己。这样不管__block怎么复制到堆上,还是在栈上,都可以通过(i->__forwarding->i)来访问到变量值。

ARC环境下,一旦Block赋值就会触发copy__block就会copy到堆上,Block也是__NSMallocBlockARC环境下也是存在__NSStackBlock的时候,这种情况下,__block就在栈上。

MRC环境下,只有copy__block才会被复制到堆上,否则,__block一直都在栈上,block也只是__NSStackBlock,这个时候__forwarding指针就只指向自己了。

所以在__main_block_func_0函数里面就是写的(i->__forwarding->i)

这里还有一个需要注意的地方。还是从例子说起:

  1. 对象的变量
// ARC
int main(int argc, const char * argv[]) {
    
    @autoreleasepool {
        __block id block_obj = [[NSObject alloc]init];
        id obj = [[NSObject alloc]init];
        
        NSLog(@"block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
        
        void (^myBlock)(void) = ^{
            NSLog(@"***Block中****block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
        };
        
        myBlock();
        
    }
    return 0;
}

输出

2019-02-26 17:51:41.380964+0800 BLock[67095:13613488] block_obj = [ , 0x7ffeefbff578] , obj = [ , 0x7ffeefbff548]
2019-02-26 17:51:41.381288+0800 BLock[67095:13613488] ***Block中****block_obj = [ , 0x100624638] , obj = [ , 0x100624400]

同样来看源码:

struct __Block_byref_block_obj_0 {
  void *__isa;
__Block_byref_block_obj_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 id block_obj;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id obj;
  __Block_byref_block_obj_0 *block_obj; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_block_obj_0 *block_obj = __cself->block_obj; // bound by ref
  id obj = __cself->obj; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_538cd5_mi_1,(block_obj->__forwarding->block_obj) , &(block_obj->__forwarding->block_obj) , obj , &obj);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

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};
int main(int argc, const char * argv[]) {

    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __attribute__((__blocks__(byref))) __Block_byref_block_obj_0 block_obj = {(void*)0,(__Block_byref_block_obj_0 *)&block_obj, 33554432, sizeof(__Block_byref_block_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
        id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_538cd5_mi_0,(block_obj.__forwarding->block_obj) , &(block_obj.__forwarding->block_obj) , obj , &obj);

        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, (__Block_byref_block_obj_0 *)&block_obj, 570425344));

        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

首先需要说明的一点是对象在OC中,默认声明自带__strong所有权修饰符的

根据打印出来的结果来看,ARC环境下,Block捕获外部对象变量,是都会copy一份的,地址都不同。只不过带有__block修饰符的变量会被捕获到Block内部持有。

我们再来看看MRC环境下的情况,还是将上述代码的例子运行在MRC中。

输出:

2019-02-26 17:59:16.199394+0800 BLock[67192:13619032] block_obj = [ , 0x7ffeefbff578] , obj = [ , 0x7ffeefbff548]
2019-02-26 17:59:16.199719+0800 BLock[67192:13619032] ***Block中****block_obj = [ , 0x7ffeefbff578] , obj = [ , 0x7ffeefbff520]

这个时候block在栈上,__NSStackBlock__,可以打印出来retainCount值都是1。当把这个block copy一下,就变成__NSMallocBlock__,对象的retainCount值就会变成2了。

在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以才会产生循环引用的问题!
在ARC环境下,对于没有声明为__block的外部对象,也会被retain。

循环引用的由来

当A对象里面强引用了B对象,B对象又强引用了A对象,两者的retainCount值一直都无法为0,内存始终无法释放,导致内存泄露,就是本应该释放的对象,在其生命周期结束之后依旧存在。三个对象之间也可能存在相互引用,也有一个对象自身的循环引用。当一个对象内部的一个obj,强引用的自身,也会导致循环引用的问题出现。常见的就是block里面引用的问题

_weak, _strong 实现原理

ARC环境下,id类型和对象类型和C语言其他类型不同,类型前必须加上所有权的修饰符:

  1. __strong修饰符(默认)
  2. __weak修饰符
  3. __unsafe_unretained修饰符
  4. __autoreleasing修饰符
__strong 的实现原理(详见ARC详细)
  1. 对象持有自己
id __strong strong_obj =[ [NSObject alloc] init];

clang 以下得到的结果

id __attribute__((objc_ownership(strong))) strong_obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));

相应的会调用

id strong_obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(strong_obj,selector(init));
objc_release(strong_obj); // ARC 下会自动插入Release代码,在作用域结束的时候自动释放
  1. 对象不持有自己
    生成对象的时候不用alloc/new/copy/mutableCopy等方法
{
    id __strong array_obj = [NSMutableArray array];
}

clang一下

id __attribute__((objc_ownership(strong))) strong_obj = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));

相应的调用

id array_obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(array_obj);
objc_release(array_obj);

与之前对象会持有自己的情况不同,这里多了一个objc_retainAutoreleasedReturnValue函数
在ARC详细文档中可知objc_retainAutoreleasedReturnValue函数是LLVM编译器的一个优化objc_retainAutoreleasedReturnValue,是用于自己持有(retain)对象的函数,它持有的对象应为返回注册在autoreleasepool中对象的方法或者是函数的返回值

还有俩个函数和objc_retainAutoreleasedReturnValue功能类似
1 id objc_autoreleaseReturnValue(id value)
2 id objc_retainAutoreleasedReturnValue(id value)
ARC中原本对象生成之后是要注册到autoreleasepool中,但是调用了objc_autoreleasedReturnValue之后,紧接着调用了 objc_retainAutoreleasedReturnValueobjc_autoreleasedReturnValue函数会去检查该函数方法或者函数调用方的执行命令列表,如果里面有objc_retainAutoreleasedReturnValue()方法,那么该对象就直接返回给方法或者函数的调用方。达到了即使对象不注册到autoreleasepool中,也可以返回拿到相应的对象

__weak实现原理
{
    id __weak weak_obj = strongObj; // 声明一个weak对象
}

clang 一下

id __attribute__((objc_ownership(none))) weak_obj = strongObj;

相应调用

id weak_obj;
objc_initWeak(& weak_obj,strongObj);
objc_destoryWeak(& weak_obj);

根据ARC文档描述objc_initWeak函数实现如下:

id objc_initWeak(id *object, id value) {   
    *object = nil; 
    return objc_storeWeak(object, value);
}

把传入的object变成0或者nil,然后执行objc_storeWeak函数

objc_storeWeak函数实现为:

void objc_destroyWeak(id *object) { 
    objc_storeWeak(object, nil);
}

也是会去调用objc_storeWeak函数。objc_initWeakobjc_destroyWeak函数都会去调用objc_storeWeak函数,唯一不同的是调用的入参不同,一个是value,一个是nil
objc_storeWeak 函数实现
objc_storeWeak函数的用途就很明显了。由于weak表也是用Hash table实现的,所以objc_storeWeak函数就把第一个入参的变量地址注册到weak表中,然后根据第二个入参来决定是否移除。如果第二个参数为0,那么就把__weak变量从weak表中删除记录,并从引用计数表中删除对应的键值记录。

所以如果__weak引用的原对象如果被释放了,那么对应的__weak对象就会被指为nil。原来就是通过objc_storeWeak函数这些函数来实现的

weakSelf、strongSelf的用途

先看一个循环引用的例子
.h文件

#import 
typedef void(^Run)();
@interface Person : NSObject
@property (copy , nonatomic) NSString *name;
@property (copy , nonatomic) Run run;
@end

.m文件

#import "ViewController.h"
#import "Person.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
  
    Person *person = [[Person alloc]init];
    person.name = @"Mr.Peng";

    person.run = ^{
        NSLog(@"%@ start running", person.name);
    };
}

到这里,大家应该看出来了,这里肯定出现了循环引用了。personrunBlock里面强引用了student自身。根据block的分析,可知,_NSConcreteMallocBlock捕获了外部的对象,会在内部持有它。retainCount值会加一(可以通过Leak Checks检查工具查看相互引用的对象)
而将run修改为如下时

    person.run = ^(NSString *name){
        NSLog(@"%@ start running",name);
    };

不会引起循环引用,因为block不会捕获形参到block内部进行持有
再看下面的例子:

@interface ViewController ()
@property (copy,nonatomic) NSString *name;
@property (strong, nonatomic) Person *person;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person * person = [[Person alloc]init];
    
    self.name = @"halfrost";
    self.person = person;
    
    person.run = ^{
        NSLog(@"%@ start running", self.name);
    };
    
    person.run();
}

ViewController虽然强引用着person,但是person里面的blcok强引用的是viewControllername属性,并没有形成环。如果把上述的self.name改成self,也依旧不会产生循环引用。因为他们都没有强引用这个block

有如下代码:

  Person * person = [[Person alloc]init];
    
    __block Person * bperson = person;
    person.name = @"Mr.Peng";
    person.run = ^{
        NSLog(@"%@ start running", bperson.name);
        bperson = nil;
    };

这段代码会循环么,看起来不会,但实际上会,由于没有执行personblockperson持有block,block持有__block变量,__block变量又持有student对象,形成环,导致循环引用,想要解决循环引用,断掉其中的环就行了person.run()执行block 即可

** 值得注意的是,在ARC__block会导致对象被retain,有可能导致循环引用。而在MRC下,则不会retain这个对象,也不会导致循环引用

正式来看看weakSelfstrongSelf的用法

  1. weakSelf
#define WEAKSELF typeof(self) __weak weakSelf = self; 这是我们平时的写法。
    Person * person = [[Person alloc]init];
    __block Person * bperson = person;
    person.name = @"Mr.Peng";
    person.run = ^{
        NSLog(@"%@ start running", bperson.name);
    };
    person.run()

这段代码因为block还持有bperson,会引起循环,改成weakSelf模式:

    Person * person = [[Person alloc]init];
    __weak typeof(person) weakSelf = student;
    person.name = @"Mr.Peng";
    person.run = ^{
        NSLog(@"%@ start running", weakSelf.name);
    };
    person.run()

解决循环应用的问题一定要分析清楚哪里出现了循环引用,只需要把其中一环加上weakSelf这类似的宏,就可以解决循环引用。如果分析不清楚,就只能无脑添加weakSelfstrongSelf,这样的做法不可取

  1. strongSelf
    上面介绍完了weakSelf,既然weakSelf能完美解决循环引用问题,那为何还需要strongSelf呢?
    Person * person = [[Person alloc]init];
    __weak typeof(person) weakSelf = student;
    person.name = @"Mr.Peng";
    person.run = ^{
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@ start running", weakSelf.name);
          });
        
    };
    person.run()

输出:

(null)start running

为什么输出是这样的呢?
dispatch_after这个函数里面。在run()block结束之后,person被自动释放了。又由于dispatch_after里面捕获的__weak的person,根据__weak的实现原理,在原对象释放之后,__weak对象就会变成null,防止野指针。所以就输出了null了。
那么怎么才能在weakSelf之后,block里面还能继续使用weakSelf之后的对象呢?
究其根本原因就是weakSelf之后,无法控制什么时候会被释放,为了保证在block内不会被释放,需要添加__strong

block里面使用的__strong修饰的weakSelf是为了在函数生命周期中防止self提前释放。strongSelf是一个自动变量当block执行完毕就会释放自动变量strongSelf不会对self进行一直进行强引用。

至此,我们就明白了weakSelfstrongSelf的用途了。
weakSelf 是为了block不持有self,避免Retain Circle循环引用。在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf
strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放,就需要加入strongSelfblock执行完后这个strongSelf会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问self,则需要使用strongSelf

@weakify、@strongify实现原理

学习完了weakSelfstrongSelf之后,接下来再学习学习@weakify、@strongify,这两个关键字是ARC中避免Block循环引用而开发的2个宏,这2个宏的实现过程很牛,值得我们学习。

@weakify@strongify的作用和weakSelfstrongSelf对应的一样。这里我们具体看看大神是怎么实现这2个宏的。

直接从源码看起来。

#define weakify(...) \
    rac_keywordify \
    metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)


#define strongify(...) \
    rac_keywordify \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
    _Pragma("clang diagnostic pop")

看到这种宏定义,咋一看什么都不知道。那就只能一层层的往下看。

  1. weakify
    先从weakify(...)开始。
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

这里在debug模式下使用@autoreleasepool是为了维持编译器的分析能力,而使用@try/@catch 是为了防止插入一些不必要的autoreleasepoolrac_keywordify 实际上就是autoreleasepool {}
的宏替换。因为有了autoreleasepool {}的宏替换,所以weakify要加上@,形成@autoreleasepool {}

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

__VA_ARGS__:总体来说就是将左边宏中 ... 的内容原样抄写在右边 __VA_ARGS__所在的位置。它是一个可变参数的宏,是新的C99规范中新增的,目前似乎只有gcc支持(VC从VC2005开始支持)。

那么我们使用@weakify(self)传入进去。__VA_ARGS__相当于self。此时我们可以把最新开始的weakify套下来。于是就变成了这样:

rac_weakify_,, __weak, __VA_ARGS__整体替换MACRO, SEP, CONTEXT, ...

这里需要注意的是,源码中就是给的两个","逗号是连着的,所以我们也要等效替换参数,相当于SEP是空值。

替换完成之后就是下面这个样子:

autoreleasepool {}
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(self))(rac_weakify_, , __weak, self)

现在我们需要弄懂的就是metamacro_concatmetamacro_argcount是干什么用的。

继续看看metamacro_concat 的实现


#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)

#define metamacro_concat_(A, B) A ## B

##是宏连接符。举个例子:

假设宏定义为#define XNAME(n) x##n,代码为:XNAME(4),则在预编译时,宏发现XNAME(4)XNAME(n)匹配,则令 n4,然后将右边的n的内容也变为4,然后将整个XNAME(4)替换为 x##n,亦即 x4,故 最终结果为 XNAME(4) 变为 x4。所以A##B就是AB

metamacro_argcount 的实现

#define metamacro_argcount(...) \
        metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__VA_ARGS__)

metamacro_concat是上面讲过的连接符,那么metamacro_at, N = metamacro_atN,由于N = 20,于是metamacro_atN = metamacro_at20。

#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)

metamacro_at20的作用就是截取前20个参数,剩下的参数传入metamacro_head

Objective-C

#define metamacro_head(...) \
        metamacro_head_(__VA_ARGS__, 0)

#define metamacro_head_(FIRST, ...) FIRST

metamacro_head的作用返回第一个参数。返回到上一级metamacro_at20,如果我们从最源头的@weakify(self),传递进来,那么metamacro_at20(self,20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1),截取前20个参数,最后一个留给metamacro_head_(1),那么就应该返回1。

metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(self)) = metamacro_concat(metamacro_foreach_cxt, 1) 最终可以替换成metamacro_foreach_cxt1。

在源码中继续搜寻。

// metamacro_foreach_cxt expansions
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)

#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
    SEP \
    MACRO(1, CONTEXT, _1)

#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
    metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    SEP \
    MACRO(2, CONTEXT, _2)

#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
    metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
    SEP \
    MACRO(3, CONTEXT, _3)

#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
    metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
    SEP \
    MACRO(4, CONTEXT, _4)

#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
    metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
    SEP \
    MACRO(5, CONTEXT, _5)

#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
    metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
    SEP \
    MACRO(6, CONTEXT, _6)

#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
    metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
    SEP \
    MACRO(7, CONTEXT, _7)

#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
    metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
    SEP \
    MACRO(8, CONTEXT, _8)

#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
    metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
    SEP \
    MACRO(9, CONTEXT, _9)

#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
    metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
    SEP \
    MACRO(10, CONTEXT, _10)

#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
    metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
    SEP \
    MACRO(11, CONTEXT, _11)

#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
    metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
    SEP \
    MACRO(12, CONTEXT, _12)

#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
    metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
    SEP \
    MACRO(13, CONTEXT, _13)

#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
    metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
    SEP \
    MACRO(14, CONTEXT, _14)

#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
    metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
    SEP \
    MACRO(15, CONTEXT, _15)

#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
    metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
    SEP \
    MACRO(16, CONTEXT, _16)

#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
    metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
    SEP \
    MACRO(17, CONTEXT, _17)

#define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
    metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
    SEP \
    MACRO(18, CONTEXT, _18)

#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
    metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
    SEP \
    MACRO(19, CONTEXT, _19)
metamacro_foreach_cxt这个宏定义有点像递归,这里可以看到N 最大就是20,于是metamacro_foreach_cxt19就是最大,metamacro_foreach_cxt19会生成rac_weakify_(0,__weak,_18),然后再把前18个数传入metamacro_foreach_cxt18,并生成rac_weakify_(0,__weak,_17),依次类推,一直递推到metamacro_foreach_cxt0。
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
metamacro_foreach_cxt0就是终止条件,不做任何操作了。

于是最初的@weakify就被替换成

autoreleasepool {}
metamacro_foreach_cxt1(rac_weakify_, , __weak, self)

#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)

代入参数

autoreleasepool {}
rac_weakify_(0,__weak,self)

最终需要解析的就是rac_weakify_

#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

把(0,__weak,self)的参数替换进来(INDEX, CONTEXT, VAR)。
INDEX = 0, CONTEXT = __weak,VAR = self,

于是

CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

等效替换为

__weak __typeof__(self) self_weak_ = self;
最终@weakify(self) = __weak __typeof__(self) self_weak_ = self;

这里的self_weak_ 就完全等价于我们之前写的weakSelf

  1. strongify
    再继续分析strongify(...)

rac_keywordify还是和weakify一样,是autoreleasepool {},只为了前面能加上@

_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
_Pragma("clang diagnostic pop")

strongifyweakify多了这些_Pragma语句。

关键字_PragmaC99里面引入的。_Pragma#pragma(在设计上)更加合理,因而功能也有所增强。

上面的等效替换

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic pop

这里的clang语句的作用:忽略当一个局部变量或类型声明遮盖另一个变量的警告。

最初的

#define strongify(...) \
    rac_keywordify \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
    _Pragma("clang diagnostic pop")

strongify里面需要弄清楚的就是metamacro_foreachrac_strongify_

#define metamacro_foreach(MACRO, SEP, ...) \
        metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
#define rac_strongify_(INDEX, VAR) \
    __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);

我们先替换一次,SEP = 空 , MACRO =rac_strongify___VA_ARGS__ , 于是替换成这样。

metamacro_foreach_cxt(metamacro_foreach_iter,,rac_strongify_,self)

根据之前分析,metamacro_foreach_cxt再次等效替换,metamacro_foreach_cxt##1(metamacro_foreach_iter,,rac_strongify_,self)

根据

#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
再次替换成metamacro_foreach_iter(0, rac_strongify_, self)

继续看看metamacro_foreach_iter的实现

#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG)

最终替换成rac_strongify_(0,self)


#define rac_strongify_(INDEX, VAR) \
    __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
INDEX = 0, VAR = self,于是@strongify(self)就等价于

 __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);

等价于

__strong __typeof__(self) self = self_weak_;

注意@strongify(self)只能使用在block中,如果用在block外面,会报错,因为这里会提示你Redefinition of 'self'

总结一下

@weakify(self) = @autoreleasepool{} __weak __typeof__ (self) self_weak_ = self;

@strongify(self) = @autoreleasepool{} __strong __typeof__(self) self = self_weak_;

经过分析以后,其实@weakify(self)@strongify(self) 就是比我们日常写的weakSelfstrongSelf多了一个@autoreleasepool{}而已,至于为何要用这些复杂的宏定义来做,目前还没有理解

你可能感兴趣的:(深入 Block)