Block详解

作者: 鼬殿 | 来源:发表于2020-08-06 18:41 被阅读0次

    block的本质

    先看block的简单实现

     int age = 20;
     void (^block)(int) =  ^(int a){
          NSLog(@"this is a block! -- %d", age);
     };
     block(10);
    

    转为C++代码

    struct __main_block_desc_0 {
        size_t reserved;
    *********************************
    block结构体占用的内存大小
    *********************************
        size_t Block_size; 
    };
    
    struct __block_impl {
        void *isa; isa指针
        int Flags;
        int Reserved;
    *********************************
    block内执行的代码会封装到一个函数中,FuncPtr存放函数的内存地址
    *********************************
        void *FuncPtr; 
    };
    
    struct __main_block_impl_0 {
        struct __block_impl impl;
    *********************************
    block的描述
    *********************************
      struct __main_block_desc_0* Desc; 
    *********************************
    持有外部变量 age
    *********************************
        int age; 
    };
    
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int age;
    *********************************
    构造函数(类似OC的init)返回结构体对象
    *********************************
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    *********************************
    封装了block执行逻辑的函数
    *********************************
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a) {
      int age = __cself->age; // bound by copy
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_22e326_mi_0, age);
    
    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)};
    

    查看Block的继承关系

    *********************************
     __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
    *********************************
     void (^block)(void) = ^{
                  NSLog(@"Hello");
              };
     NSLog(@"%@", [block class]);
     NSLog(@"%@", [[block class] superclass]);
     NSLog(@"%@", [[[block class] superclass] superclass]);
     NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
    
    __NSGlobalBlock__
    __NSGlobalBlock
    NSBlock
    NSObject
    

    结论:

    block本质上也是一个OC对象,它内部也有个isa指针
    block是封装了函数调用以及函数调用环境的OC对象

    block的变量捕获(capture)

    为了保证block内部能够正常访问外部的变量,block有个变量捕获机制


    验证:

    void (^block)(void);
    
    int weight_ = 10;
    static int staticWeight_ = 10;
    
    void test()
    {
    *********************************
        自动变量,离开作用域就销毁, auto默认自带,可以不写
    *********************************
        auto int age = 10;
        static int height = 10;
        
        block = ^{
            // age的值捕获进来(capture)
            NSLog(@"age is %d, height is %d, weight_ is %d,staticWeight_ is %d", age, height,weight_,staticWeight_);
        };
        age = 20;
        height = 20;
        weight_ = 20;
        staticWeight_ = 20;
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            test();
            block();
            return 0;
        }
    }
    

    打印结果

    age is 10, height is 20, weight_ is 20,staticWeight_ is 20
    

    转为C++源码如下

    void (*block)(void);
    
    int weight_ = 10;
    static int staticWeight_ = 10;
    *********************************
    __test_block_impl_0结构体中并没有捕获全局变量
    *********************************
    struct __test_block_impl_0 {
      struct __block_impl impl;
      struct __test_block_desc_0* Desc;
      int age;
      int *height;
      __test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
      int age = __cself->age; // bound by copy
      int *height = __cself->height; // bound by copy
    
    *********************************
    age是值传递
    *height 是指针传递,存储的是外部变量的地址
    weight_和staticWeight_参数直接调用全局变量,并没有捕获
    *********************************
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_a117a3_mi_0, age, (*height),weight_,staticWeight_);
        }
    
    static struct __test_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __test_block_desc_0_DATA = { 0, sizeof(struct __test_block_impl_0)};
    
    
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            test();
            ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
            return 0;
        }
    }
    

    思考:在block里面访问self呢?

    @interface Person : NSObject
    @property (copy, nonatomic) NSString *name;
    
    - (void)test;
    
    @end
    
    #import "Person.h"
    
    @implementation Person
    
    - (void)test
    {
        void (^block)(void) = ^{
            NSLog(@"%@---%@----%@",self, self->_name, [self name]);
        };
        block();
    }
    
    @end
    

    转为C++代码如下

    *********************************
    block捕获了self
    *********************************
    struct __Person__test_block_impl_0 {
      struct __block_impl impl;
      struct __Person__test_block_desc_0* Desc;
      Person *self;
      __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
      Person *self = __cself->self; // bound by copy
    *********************************
    block通过捕获的self调用self的成员变量和方法
    *********************************
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_Person_606d56_mi_0,self, (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)), ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("name")));
        }
    static void __Person__test_block_copy_0(struct __Person__test_block_impl_0*dst, struct __Person__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __Person__test_block_dispose_0(struct __Person__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static struct __Person__test_block_desc_0 {
      size_t reserved;
      size_t Block_size;
      void (*copy)(struct __Person__test_block_impl_0*, struct __Person__test_block_impl_0*);
      void (*dispose)(struct __Person__test_block_impl_0*);
    } __Person__test_block_desc_0_DATA = { 0, sizeof(struct __Person__test_block_impl_0), __Person__test_block_copy_0, __Person__test_block_dispose_0};
    *********************************
    OC转C默认传递两个参数self 和 SEL _cmd
    参数就是局部变量,所以block会捕获self
    *********************************
    static void _I_Person_test(Person * self, SEL _cmd) {
        void (*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    

    block的类型

    block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
    NSGlobalBlock ( _NSConcreteGlobalBlock )
    NSStackBlock ( _NSConcreteStackBlock )
    NSMallocBlock ( _NSConcreteMallocBlock )



    每一种类型的block调用copy后的结果如下所示

    验证:

    将工程置于MRC下(在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上)


    void test()
    {
        // Global:没有访问auto变量
        void (^block1)(void) = ^{
        };
        NSLog(@"没有访问auto变量block类型---------%@",block1);
        NSLog(@"copy后---------%@",[block1 copy]);
        // Stack:访问了auto变量
        int age = 10;
        void (^block2)(void) = ^{
            NSLog(@"age---------%d", age);
        };
        NSLog(@"访问auto变量block类型---------%@", block2);
        NSLog(@"copy后-%@", [block2 copy]);
    }
    

    调用

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            test();
        }
        return 0;
    }
    
    

    结果如下:

    没有访问auto变量block类型---------<__NSGlobalBlock__: 0x1000020a8>
    copy后---------<__NSGlobalBlock__: 0x1000020a8>
    
    访问auto变量block类型---------<__NSStackBlock__: 0x7ffeefbff3e0>
    copy后-<__NSMallocBlock__: 0x10051b360>
    

    block的copy

    在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

    • block作为函数返回值时
    typedef void (^Block)(void);
    
    Block myblock()
    {
        int age = 10;
        return ^{
            NSLog(@"---------%d",age);
        };
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Block block = myblock();
            block();
            NSLog(@"%@", [block class]);
        }
        return 0;
    }
    
    ---------10
    __NSMallocBlock__
    
    • 将block赋值给__strong指针时
    typedef void (^Block)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            int age = 10;
            Block block = ^{
                NSLog(@"---------%d", age);
            };
            NSLog(@"%@", [block class]);
        }
        return 0;
    }
    
    __NSMallocBlock__
    
    • block作为Cocoa API中方法名含有usingBlock的方法参数时
    NSArray *arr = @[];
    [arr sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
            }];
    
    • block作为GCD API的方法参数时
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            });
    

    block作为属性变量的写法

    MRC下block属性的建议写法

    @property (copy, nonatomic) void (^block)(void);
    

    ARC下block属性的建议写法

    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);
    

    对象类型的auto变量

    • 当block内部访问了对象类型的auto变量时
      如果block是在栈上,将不会对auto变量产生强引用

    验证:(MRC环境下)

    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    @property (assign, nonatomic) int age;
    @end
    
    #import "Person.h"
    
    @implementation Person
    
    - (void)dealloc
    {
        [super dealloc];
        NSLog(@"Person - dealloc");
    }
    
    @end
    

    调用auto修辞的person对象,并设置断点

    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    typedef void (^Block)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Block block;
            {
                Person *person = [[Person alloc] init];
                person.age = 10;
                block = ^{
                    NSLog(@"---------%d", person.age);
                };
                NSLog(@"%@",[block class]);
                [person release];
            }
          *******************************
            断点处
          *******************************
            NSLog(@"------");
        }
        return 0;
    }
    

    打印如下

    __NSStackBlock__
    Person - dealloc
    

    可以看到block未释放时,person对象已经被提前释放,说明block并没有对person产生强引用

    • 如果block被拷贝到堆上
    1. 会调用block内部的copy函数
    2. copy函数内部会调用_Block_object_assign函数
    3. _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

    验证:(ARC环境下)

    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    @property (assign, nonatomic) int age;
    @end
    
    #import "Person.h"
    
    @implementation Person
    
    - (void)dealloc
    {
    //    [super dealloc];
        NSLog(@"Person - dealloc");
    }
    
    @end
    
    
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    typedef void (^Block)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Block block;
            {
                Person *person = [[Person alloc] init];
                person.age = 10;
    //            __weak Person *weakPerson = person;
                block = ^{
                    NSLog(@"---------%d", person.age);
                };
                NSLog(@"%@",[block class]);
            }
          *******************************
            断点处
          *******************************
            NSLog(@"------");
        }
        return 0;
    }
    

    打印如下

    __NSMallocBlock__
    

    可以看到block未释放时,person对象也没有释放

    源码解读:

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
    *********************************
    如果auto对象没有用_weak修辞,默认就是__strong
    *********************************
      Person *__strong person;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    *********************************
    封装了block执行逻辑的函数
    *********************************
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      Person *__strong person = __cself->person; // bound by copy
    
                    NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_07fbbe_mi_0, ((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("age")));
                }
    *********************************
    根据auto对象类型变量的修饰符(__strong、__weak、__unsafe_unretained)
    做出相应的操作,形成强引用(retain)或者弱引用
    *********************************
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    *********************************
    释放引用的auto对象类型变量
    *********************************
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    *********************************
    block的描述,一旦访问的是对象类型,就会多了__main_block_copy_0,
    和 __main_block_dispose_0函数,对对象类型变量的引用和释放
    *********************************
    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};
    

    思考,如果用

     __weak Person *weakPerson = person;
    

    呢?
    结果就是

    __NSMallocBlock__
    Person - dealloc
    
    • 如果block从堆上移除
    1. 会调用block内部的dispose函数
    2. dispose函数内部会调用_Block_object_dispose函数
    3. _Block_object_dispose函数会自动释放引用的auto变量(release)
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    typedef void (^Block)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Block block;
            {
                Person *person = [[Person alloc] init];
                person.age = 10;
                
               // __weak Person *weakPerson = person;
                block = ^{
                    NSLog(@"---------%d", person.age);
                };
                NSLog(@"%@",[block class]);
            }
            NSLog(@"------");
        }
        *******************************
            断点处
        *******************************
        return 0;
    }
    

    结果如下:

    __NSMallocBlock__
    ------
    Person - dealloc
    

    源码解读在上面第2步

    __weak问题解决

    在使用clang转换OC为C++代码时,可能会遇到以下问题

    cannot create __weak reference in file using manual reference
    

    解决方案:支持ARC、指定运行时系统版本,比如

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
    

    __block修饰符

    __block可以用于解决block内部无法修改auto变量值的问题
    __block不能修饰全局变量、静态变量(static

    typedef void (^Block)(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            __block int age = 10;
            Block block = ^{
                age = 20;
                NSLog(@"age is %d", age);
            };
            block();
        }
        return 0;
    }
    

    转为底层C++代码

    ***************************
    编译器把__block变量age包装成一个对象,转变C++ 就是__Block_byref_age_0结构体
    __forwarding指针指向__Block_byref_age_0结构体
    ***************************
    struct __Block_byref_age_0 {
      void *__isa;
    __Block_byref_age_0 *__forwarding;
     int __flags;
     int __size;
    ***************************
    捕获的age的内存地址和外部变量age的内存地址是一样的
    ***************************
     int age;
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
    ***************************
    block内部持有的 __Block_byref_age_0 类型的age指针指向上面的结构体
    这里是强引用关系
    ***************************
      __Block_byref_age_0 *age; // by ref
      ...
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_age_0 *age = __cself->age; // bound by ref
    ***************************
    block内部修改age变量的值会先找到__Block_byref_age_0结构体
    ->__forwarding指针->age
    ***************************
                (age->__forwarding->age) = 20;
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r_gswfk35n5938fbdhf6s4xw_c0000gp_T_main_3520b8_mi_0, (age->__forwarding->age));
            }
    
    
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
                                                              (void*)0,
                                                              ***************************
                                                              对应的是__Block_byref_age_0 *__forwarding;
                                                              说明*_forwarding接收的是age的内存地址
                                                              ***************************
                                                              (__Block_byref_age_0 *)&age, 
                                                              0, 
                                                              sizeof(__Block_byref_age_0), 
                                                              10
                                                              };
           ...
        }
        return 0;
    }
    

    编译器会将__block变量包装成一个对象

    __block的内存管理

    • 当block在栈上时,并不会对__block变量产生强引用

    • 当block被copy到堆时

    1. 会调用block内部的copy函数
    2. copy函数内部会调用_Block_object_assign函数
    3. _Block_object_assign函数会对__block变量形成强引用(retain)
    • 当block从堆中移除时
    1. 会调用block内部的dispose函数
    2. dispose函数内部会调用_Block_object_dispose函数
    3. _Block_object_dispose函数会自动释放引用的__block变量(release)

    __block的__forwarding指针

    对象类型的auto变量、__block变量

    • 当block在栈上时,对它们都不会产生强引用

    • 当block拷贝到堆上时,都会通过copy函数来处理它们
      __block变量(假设变量名叫做a)

    _Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
    

    对象类型的auto变量(假设变量名叫做p)

    _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
    
    • 当block从堆上移除时,都会通过dispose函数来释放它们
      __block变量(假设变量名叫做a)
    _Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
    

    对象类型的auto变量(假设变量名叫做p)

    _Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
    

    __block修饰的对象类型

    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    typedef void (^Block) (void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            __block Person *person = [[Person alloc] init];
            Block block = ^{
                NSLog(@"%p", person);
            };
            block();
        }
        return 0;
    }
    

    转为C++底层源码

    *************************
    __Block修辞的person对象转成C++变成了__Block_byref_person_0结构体
    *************************
    struct __Block_byref_person_0 {
      void *__isa; 指针占用8个字节内存空间
    __Block_byref_person_0 *__forwarding; 8
     int __flags; 4
     int __size; 4
    *************************
     __Block_byref_person_0结构体中持有person对象
     copy和dispose函数是对person对象的引用和释放
    *************************
     void (*__Block_byref_id_object_copy)(void*, void*); 8
     void (*__Block_byref_id_object_dispose)(void*); 8
    *************************
    __Block_byref_person_0结构体内存地址+40就是person的内存地址
    在MRC情况下不会对person强引用
    *************************
     Person *person;
    };
    
    static void __Block_byref_id_object_copy_131(void *dst, void *src) {
     _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    }
    static void __Block_byref_id_object_dispose_131(void *src) {
     _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    }
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_person_0 *person; // by ref
    ...
    };
    
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    *************************
    对__Block_byref_person_0 *person的内存管理操作
    *************************
      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};
    

    结论:

    • 当__block变量在栈上时,不会对指向的对象产生强引用

    • 当__block变量被copy到堆时

    1. 会调用__block变量内部的copy函数
    2. copy函数内部会调用_Block_object_assign函数
    3. _Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain
    • 如果__block变量从堆上移除
    1. 会调用__block变量内部的dispose函数
    2. dispose函数内部会调用_Block_object_dispose函数
    3. _Block_object_dispose函数会自动释放指向的对象(release)

    循环引用问题

    #import <Foundation/Foundation.h>
    
    typedef void (^Block) (void);
    
    @interface Person : NSObject
    @property (copy, nonatomic) Block block;
    
    - (void)test;
    
    @end
    
    #import "Person.h"
    
    @implementation Person
    
    - (void)dealloc
    {
    ********************
    MRC打开 //[super dealloc];
    ********************
        NSLog(@"%s", __func__);
    }
    
    - (void)test
    {
        self.block = ^{
            NSLog(@"%@", self);
        };
    }
    @end
    
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *person = [[Person alloc] init];
            [person test];
        }
    **********************
    设置断点
    **********************
        NSLog(@"*****");
        return 0;
    }
    

    可以看到在block函数执行完毕后,dealloc函数并没有执行,person对象没有释放

    关于block持有self的问题,上面从C++源码角度已经解读就不在说了

    解决循环引用问题 - ARC

    • 用__weak、__unsafe_unretained解决
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf);
    };
    
     __unsafe_unretained typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"%@", weakSelf);
    };
    

    __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil

    __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
    
    • 用__block解决(必须要调用block)
    #import <Foundation/Foundation.h>
    #import "Person.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            __block Person *person = [[Person alloc] init];
            person.block = ^{
                NSLog(@"%@", person);
                person = nil;
            };
            person.block();
        }
        NSLog(@"*****");
        return 0;
    }
    

    解决循环引用问题 - MRC

     __unsafe_unretained typeof(self) weakSelf = self;
    self.block = [^{
          //__strong typeof(weakSelf) strongSelf = weakSelf;
          NSLog(@"%@", weakSelf);
    } copy];
    [self release];
    
    __block typeof(self) weakSelf = self;
    self.block = [^{
    //__strong typeof(weakSelf) strongSelf = weakSelf;
         NSLog(@"%@", weakSelf);
    } copy];
    [self release];
    

    相关文章

      网友评论

        本文标题:Block详解

        本文链接:https://www.haomeiwen.com/subject/hqizrktx.html