Block 底层实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011374318/article/details/87472075

Block 底层实现原理

在理解 Block 的底层实现原理的过程中,可以通过 clang 命令将一个 block 例子转换为 C 代码,从而解读其具体的代码实现逻辑。

clang main.m -rewrite-objc -o dest.cpp

上面的命令,是将 main.m 文件源码转换为 C 语言代码并存储在 dest.cpp 文件中。

理解 block 的实现

  1. 第一步,准备一个 main.m 文件,包含一个 block 定义。

    int main(int argc, char * argv[]) {
        void (^test)() = ^(){
        };
        test();
    }
    
  2. 运行 clang main.m -rewrite-objc -o dest.cpp 命令,查看转换后的代码。

    #ifndef __OBJC2__
    #define __OBJC2__
    #endif
    struct objc_selector; struct objc_class;
    struct __rw_objc_super { 
    	struct objc_object *object; 
    	struct objc_object *superClass; 
    	__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
    };
    #ifndef _REWRITER_typedef_Protocol
    typedef struct objc_object Protocol;
    #define _REWRITER_typedef_Protocol
    #endif
    #define __OBJC_RW_DLLIMPORT extern
    __OBJC_RW_DLLIMPORT void objc_msgSend(void);
    __OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);
    __OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);
    __OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);
    __OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);
    __OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *);
    __OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *);
    __OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *);
    __OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);
    __OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *);
    __OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *);
    __OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);
    #ifdef _WIN64
    typedef unsigned long long  _WIN_NSUInteger;
    #else
    typedef unsigned int _WIN_NSUInteger;
    #endif
    #ifndef __FASTENUMERATIONSTATE
    struct __objcFastEnumerationState {
    	unsigned long state;
    	void **itemsPtr;
    	unsigned long *mutationsPtr;
    	unsigned long extra[5];
    };
    __OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);
    #define __FASTENUMERATIONSTATE
    #endif
    #ifndef __NSCONSTANTSTRINGIMPL
    struct __NSConstantStringImpl {
      int *isa;
      int flags;
      char *str;
    #if _WIN64
      long long length;
    #else
      long length;
    #endif
    };
    #ifdef CF_EXPORT_CONSTANT_STRING
    extern "C" __declspec(dllexport) int __CFConstantStringClassReference[];
    #else
    __OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];
    #endif
    #define __NSCONSTANTSTRINGIMPL
    #endif
    #ifndef BLOCK_IMPL
    #define BLOCK_IMPL
    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    // Runtime copy/destroy helper functions (from Block_private.h)
    #ifdef __OBJC_EXPORT_BLOCKS
    extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
    extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
    extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];
    extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];
    #else
    __OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);
    __OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);
    __OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];
    __OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];
    #endif
    #endif
    #define __block
    #define __weak
    
    #include <stdarg.h>
    struct __NSContainer_literal {
      void * *arr;
      __NSContainer_literal (unsigned int count, ...) {
    	va_list marker;
    	va_start(marker, count);
    	arr = new void *[count];
    	for (unsigned i = 0; i < count; i++)
    	  arr[i] = va_arg(marker, void *);
    	va_end( marker );
      };
      ~__NSContainer_literal() {
    	delete[] arr;
      }
    };
    extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
    extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
    
    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void * atautoreleasepoolobj;
    };
    
    #define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
    }
    
    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, char * argv[])
    {
      void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
      									  &__main_block_desc_0_DATA));
    
      ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
    }
    static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
    
    
  3. 比较 main 函数,理解关键代码。

    void (^test)() = ^(){ }; 同下面的代码相对应

    void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
    									&__main_block_desc_0_DATA));
    

    test();((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test); 相对应。

    想理解这两段代码,要先看相关的结构体及函数。

  4. 理解相关的结构体及函数

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    结构体 __main_block_impl_0 包含一个 __block_impl 类型(结构体)的成员变量,一个指向 __main_block_desc_0 类型(结构体)的指针,以及一个构造函数。

    这个结构体的构造函数有三个参数,在初始化过程中,除了使用三个参数赋值外,还将 _NSConcreteStackBlock 的地址赋值给了 impl.isa ,查找代码可知 _NSConcreteStackBlock 是长度为 32 的指针数组。

    impl.isa = &_NSConcreteStackBlock;

    所以 impl.isa 指向一个数组,而该数组保存指针。

    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    

    结构体 __block_impl 包含四个成员变量,两个指针,两个 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)};
    

    结构体 __main_block_desc_0 包含两个成员变量,一个表示 block 所需内存的大小,一个是保留变量。

    这里直接定义了一个静态的结构体变量 __main_block_desc_0_DATA ,并且 Block_size 的值初始化为结构体 __main_block_impl_0 的大小。

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    
    }	
    

    该静态函数实际就是 block 中要执行的代码任务,这里没有要执行的代码。

  5. 理解 block 的定义和执行

    上一步理解了相关的结构体及函数后,再来理解 block 的定义及执行。

    void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
    									&__main_block_desc_0_DATA));
    

    拆分该代码如下:

    //初始化一个 __main_block_impl_0 结构体
    //参数为生成的静态函数 __main_block_func_0 以及静态变量 __main_block_desc_0_DATA 的地址
    struct __main_block_impl_0 temp = __main_block_impl_0((void *)__main_block_func_0, 
    											 &__main_block_desc_0_DATA);
    
    void (*test)() = ((void (*)())&temp);
    

    可见,block 的定义,最终是获得了一个指向一个结构体的指针。

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

    这里要注意的是 test 实际是指向 __main_block_impl_0 结构体的指针,而这里总是将其强制转转换为指向 __block_impl 结构体的指针,这样是没有问题的,因为 __main_block_impl_0 的第一个成员变量就是 __block_impl 类型的,所以两个变量的地址是一致的。而后,获取 __block_implFuncPtr 成员变量,并转换为函数地址,该地址就是生成的静态函数 __main_block_func_0 的地址,最终执行该静态函数来完成 block 中的任务。

综上所述,定义一个 block 任务,实际是生成了一个指针,同时会生成一个包含该任务的静态函数,并且该指针指向该静态函数,而 block 的调用,则是使用该指针调用其所指向的静态函数。

block 捕获局部变量

  1. 准备一个 main1.m 文件,如下

    #include <stdio.h>
    
    int main(int argc, char * argv[])
    {
    
      int value1 = 2;
      int value2 = 3;
    
      void (^test)() = ^() {
     
        int blockValue1 = value1;
        int blockValue2 = value2 + 1;	
        printf("blockValue1 = %i, blockValue2 = %i \n",blockValue1,blockValue2);
     };
    
      test();
    }
    

    在函数中定义两个局部变量,并在 block 中进行使用。

  2. 运行 clang main1.m -rewrite-objc -o dest1.cpp 命令,查看转换后的代码 dest1.cppdest.cpp 文件的区别。

    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    

    结构体 __block_impl 未发生改变

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int value1;
      int value2;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
      				 int _value1, int _value2, int flags=0) : value1(_value1), value2(_value2) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    

    结构体 __main_block_impl_0 中多了两个和局部变量同名的成员变量,并且构造函数中也多了两个参数用来对这两个成员变量赋值。

    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)};	
    

    结构体 __main_block_desc_0 未发生变化

    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int value1 = __cself->value1; // bound by copy
      int value2 = __cself->value2; // bound by copy
    
    
        int blockValue1 = value1;
        int blockValue2 = value2 + 1;
        printf("blockValue1 = %i, blockValue2 = %i \n",blockValue1,blockValue2);
    }
    

    从生成的静态函数可知,当其执行时,所获取的局部变量实际是 __main_block_impl_0 结构体中保存的成员变量。

    int main(int argc, char * argv[])
    {
    
      int value1 = 2;
      int value2 = 3;
    
      void (*test)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,
      									  &__main_block_desc_0_DATA, value1, value2));
    
      ((void (*)(__block_impl *))((__block_impl *)test)->FuncPtr)((__block_impl *)test);
    }	
    
    

    该函数同样是定义了一个指向 __main_block_impl_0 结构体类型变量的指针,而其构造函数传递了局部变量,并且是值传递,所以在该定义之后再去修改局部变量,并不会影响 block 回调执行时的变量值。

  3. 执行 gcc main1.m -o main1.out 编译命令

    得到 main1.out 文件后,执行 ./main1.out,得到如下结果:

    blockValue1 = 2, blockValue2 = 4

综上所述,block 的局部变量的使用,是将局部变量保存到结构体中,而在 block 执行时,再从结构体中获取保存的变量。

block 参数及返回值

  1. 准备一个 main2.m 文件,如下

    #import <Foundation/Foundation.h>
    #include <stdio.h>
    
    int main(int argc, char * argv[])
    {
        int value1 = 1;
        NSNumber *number1 = [NSNumber numberWithInt:2];
        
        NSNumber * (^test)(int,NSNumber *) = ^(int value2,NSNumber *number2) {
            
            int blockValue = value1;
            NSNumber *blockNumber = number1;
            int result = blockValue + value2 + blockNumber.intValue + number2.intValue;
    
            number2 = [NSNumber numberWithInt:100];
            return [NSNumber numberWithInt:result];
        };
        
        int value2 = 3;
        NSNumber *number2 = [NSNumber numberWithInt:4];
        
        NSNumber *number = test(value2,number2);
    }	
    

    定义的 block 拥有一个 NSNumber 类型的返回值,以及两个参数,比较参数使用和变量的捕获。

  2. 执行 clang main2.m -rewrite-objc -o dest2.cpp 命令,查看转换后的代码 dest2.cppdest.cpp 文件的区别。

    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    

    结构体 __block_impl 未发生改变

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int value1;
      NSNumber *number1;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value1, NSNumber *_number1, int flags=0) : value1(_value1), number1(_number1) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    在 block 中引用了外部 OC 对象,同样会在 __main_block_impl_0 中添加一个同名的对象变量,并通过构造函数进行赋值。

    extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
    extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
    
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    	_Block_object_assign((void*)&dst->number1, (void*)src->number1, 3/*BLOCK_FIELD_IS_OBJECT*/);
    }
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    	_Block_object_dispose((void*)src->number1, 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};
    
    

    结构体 __main_block_desc_0 同样发生了变化,此处多了两个函数指针成员变量,分别在 block 拷贝到堆区以及释放时调用。

    static NSNumber * _Nonnull __main_block_func_0(struct __main_block_impl_0 *__cself, int value2, NSNumber *number2) {
    
    	int value1 = __cself->value1; // bound by copy
    	NSNumber *number1 = __cself->number1; // bound by copy
    	
    	int blockValue = value1;
    	NSNumber *blockNumber = number1;
    	int result = blockValue + value2 + 
    		((int (*)(id, SEL))(void *)objc_msgSend)((id)blockNumber, sel_registerName("intValue")) + 
    		((int (*)(id, SEL))(void *)objc_msgSend)((id)number2, sel_registerName("intValue"));
    		
    	number2 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 100);
    
    	return ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (int)result);
    }
    
    

    静态函数相应的多了两个参数,并且在函数最后对 number2 进行了修改,虽然其修改并不影响原参数。而在 block 中不可修改 number1 是因为在该函数中 number1 和 block 外的 number1 并不是同一个变量。

    int main(int argc, char * argv[])
    {
        int value1 = 1;
        NSNumber *number1 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 2);
    
        NSNumber * (*test)(int,NSNumber *) = ((NSNumber * _Nonnull (*)(int, NSNumber *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value1, number1, 570425344));
    
        int value2 = 3;
        NSNumber *number2 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 4);
    
        NSNumber *number = ((NSNumber *(*)(__block_impl *, int, NSNumber *))((__block_impl *)test)->FuncPtr)((__block_impl *)test, value2, number2);
    }	
    

    同样,block 的调用,就是静态函数的执行。

block 中修改外部变量

  1. 准备一个 main3.m 文件,如下

    #import <Foundation/Foundation.h>
    #include <stdio.h>
    
    static struct testStruct {
        int num;
        void (^test)(void);
    } testS = {
        10, nil
    };
    
    int main(int argc, char * argv[])
    {
        int value1 = 1;
        __block int value2 = 2;
        NSNumber *number1 = [NSNumber numberWithInt:3];
        __block NSNumber *number2 = [NSNumber numberWithInt:4];
        
        testS.test = ^() {
            
            int blockValue = testS.num;
            
            int sum = value1 + value2 + number1.intValue + number2.intValue;
            
            value2 = 5;
            number2 = [NSNumber numberWithInt:6];
            
        };
        
        value2 = 10;
        number2 = [NSNumber numberWithInt:20];
        
        testS.test();
    }
    
    

    对比基本类型和 OC 对象,并且查看全局静态变量在 block 中的使用情况。

  2. 执行 clang main3.m -rewrite-objc -o dest3.cpp 命令,查看转换后的代码 dest3.cppdest.cpp 文件的区别。

    int main(int argc, char * argv[])
    {
        int value1 = 1;
        
        __attribute__((__blocks__(byref))) __Block_byref_value2_0 value2 = {(void*)0,(__Block_byref_value2_0 *)&value2, 0, sizeof(__Block_byref_value2_0), 2};
        
        NSNumber *number1 = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 3);
        
        __attribute__((__blocks__(byref))) __Block_byref_number2_1 number2 = {(void*)0,(__Block_byref_number2_1 *)&number2, 33554432, sizeof(__Block_byref_number2_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 4)};
    
        testS.test = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, value1, number1, (__Block_byref_value2_0 *)&value2, (__Block_byref_number2_1 *)&number2, 570425344));
        
    	(value2.__forwarding->value2) = 10;
    	(number2.__forwarding->number2) = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 20);
    	
    	((void (*)(__block_impl *))((__block_impl *)testS.test)->FuncPtr)((__block_impl *)testS.test);
    }
    
    

    首先对比 main 函数,发现使用 __block 字段修饰的变量都封装成了结构体,并同普通变量一起作为参数传递给了 __main_block_impl_0 结构体的构造函数。

    struct __Block_byref_value2_0 {
    	void *__isa;
    	__Block_byref_value2_0 *__forwarding;
    	int __flags;
    	int __size;
    	int value2;
    };
    struct __Block_byref_number2_1 {
    	void *__isa;
    	__Block_byref_number2_1 *__forwarding;
    	int __flags;
    	int __size;
    	void (*__Block_byref_id_object_copy)(void*, void*);
    	void (*__Block_byref_id_object_dispose)(void*);
    	NSNumber *number2;
    };
    

    封装实例对象的结构体相较于封装基本类型变量的结构体多了两个管理内存的函数指针成员变量。

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int value1;
      NSNumber *number1;
      __Block_byref_value2_0 *value2; // by ref
      __Block_byref_number2_1 *number2; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _value1, NSNumber *_number1, __Block_byref_value2_0 *_value2, __Block_byref_number2_1 *_number2, int flags=0) : value1(_value1), number1(_number1), value2(_value2->__forwarding), number2(_number2->__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_value2_0 *value2 = __cself->value2; // bound by ref
    	__Block_byref_number2_1 *number2 = __cself->number2; // bound by ref
    	int value1 = __cself->value1; // bound by copy
    	NSNumber *number1 = __cself->number1; // bound by copy
    	
    	int blockValue = testS.num;
    		
    	int sum = value1 + (value2->__forwarding->value2) + 
    		((int (*)(id, SEL))(void *)objc_msgSend)((id)number1, sel_registerName("intValue")) +
    		((int (*)(id, SEL))(void *)objc_msgSend)((id)(number2->__forwarding->number2), 
    											sel_registerName("intValue"));
    		
    	(value2->__forwarding->value2) = 5;
    	(number2->__forwarding->number2) = ((NSNumber * _Nonnull (*)(id, SEL, int))(void *)objc_msgSend)((id)objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 6);
    
    }
    

    block 执行时,从 __main_block_impl_0 结构体中获取封装有外部变量的结构体。

综上所述,经过 __block 关键字修饰的变量,实际会被封装到一个结构体中,所以变量所保存的真实值由该结构体持有,block 内外对该变量的操作都经由该结构体。

block 中的循环引用

  1. 准备一个 Person.m 文件,如下

    #import "Person.h"
    
    @interface Person()
    
    @property (nonatomic, strong) NSString * name;
    
    @property (nonatomic, strong) void (^test)(void);
    
    @end
    
    @implementation Person
    
    - (void)testMethod {
        self.test = ^{      
            self.name = @"han";
        };
    }
    
    @end	
    

    在该文件中,定义一个 block 类属性,并且在 block 中使用实例对象本身。

  2. 执行 clang Person.m -rewrite-objc -o person.cpp 命令,查看转化后的代码。

    static struct /*_method_list_t*/ {
    	unsigned int entsize;  // sizeof(struct _objc_method)
    	unsigned int method_count;
    	struct _objc_method method_list[5];
    } _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    	sizeof(_objc_method),
    	5,
    	{{(struct objc_selector *)"testMethod", "v16@0:8", (void *)_I_Person_testMethod},
    	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_Person_name},
    	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_Person_setName_},
    	{(struct objc_selector *)"test", "@?16@0:8", (void *)_I_Person_test},
    	{(struct objc_selector *)"setTest:", "v24@0:8@?16", (void *)_I_Person_setTest_}}
    };
    
    

    生成了一个静态的结构体变量 _OBJC_$_INSTANCE_METHODS_Person 来记录 Person 类的实例对象方法。从该结构体中可知,testMethod 方法的具体实现为 _I_Person_testMethod

    static void _I_Person_testMethod(Person * self, SEL _cmd) {
    
        ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setTest:"), 
        	((void (*)())&__Person__testMethod_block_impl_0((void *)__Person__testMethod_block_func_0, 
        								&__Person__testMethod_block_desc_0_DATA, self, 570425344)));
    }
    
    

    在该静态函数中,只是为 Person 的实例对象属性 test 赋值,这个属性是 block ,而 objc_msgSend 第三个参数就是 block 变量。如此,便回到了 block 的定义原理上来,接着查看 __Person__testMethod_block_impl_0 结构体。

    struct __Person__testMethod_block_impl_0 {
      struct __block_impl impl;
      struct __Person__testMethod_block_desc_0* Desc;
      Person *self;
      __Person__testMethod_block_impl_0(void *fp, struct __Person__testMethod_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    可知,引用 self 同引用其他变量一样,都是在构造该结构体时进行了赋值。

    static void __Person__testMethod_block_func_0(struct __Person__testMethod_block_impl_0 *__cself) {
      Person *self = __cself->self; // bound by copy
      
      ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setName:"),
       (NSString *)&__NSConstantStringImpl__var_folders_65_h_h1bqw127sgk8bxd4r5k8200000gn_T_Person_aec206_mi_0);
    }
    

    在由 block 任务生成的静态函数中,会生成一个局部变量,该变量由结构体中的 self 成员变量赋值。

    那么这里为什么会造成循环引用呢!

    在结构体 __Person__testMethod_block_impl_0 构造时,传递了 self 指针对成员变量 self 进行赋值,默认是强引用,会增加 Person 实例的引用计数。另外,block 又赋值给了 Person 的实例对象属性。两者相互持有,那么都无法释放。

    使用 __weak__strong 进行修饰

    - (void)testMethod {
    	__weak typeof(self) weakSelf = self;
    	self.test = ^{
    		__strong typeof(weakSelf) strongSelf = weakSelf;
    		strongSelf.name = @"han";
    	};
    }
    

    如此,在 block 中使用的外部变量是经过 __weak 修饰的 weakSelf 变量,所以结构体中的成员变量从 self 变成了 weakSelf ,并且在构造过程中不会增加实例对象的引用计数值。

    而在 block 执行时,为了避免在运行过程中实例对象的释放,则需要使用 __strong 字段修饰,获取一个局部变量 strongSelf 强引用实例对象,直到 block 执行结束。


参考博文:chenxianming一缕殇流化隐半边冰霜J_Knight

猜你喜欢

转载自blog.csdn.net/u011374318/article/details/87472075