iOS 底层解析-----Block (下)

本文解决问题:(如无特殊说明,均指的ARC下编译)

  1. Block相关修饰符 __block __weak __strong __unsafe_unretained 作用
  2. Block的循环引用

在iOS 底层解析-----Block (上)中讲解到变量捕获,那么对于OC对象类型的操作如何

typedef void (^MJBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
          MJPerson *person = [[MJPerson alloc] init];
          person.age = 10;
          MJBlock block = ^{
                NSLog(@"---------%d", person.age);
            };
    }
    return 0;
}
------------编译后的__main_block_impl_0----------
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  MJPerson *person;//捕获到内部的是person对象
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

延伸 如果给person 对象增加__weak 或者  __strong 修饰符则捕获后变化为 
MJPerson * __weak person;  
MJPerson * __strong person;
--------编译后的__main_block_desc_0--------
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
//以下两个方法为对象内存管理的方法
//ARC下当Block从栈中拷贝到堆中的时候会自动调用该copy方法,如果是__strong 形成强引用 __weak __unsafe_unretained形成弱引用
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
//当Block从堆中移除时,dispose函数内部会自动释放引用的变量(release)
  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};

总结:
对象类型的auto变量.png
我们都知道用__block 可以修改基本变量类型内部的值
typedef void (^MJBlock)(void);

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

------------编译后的__main_block_impl_0----------
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
//编译器将__block 修饰的变量包装成一个对象
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//__block 修饰的变量包装成一个对象 该对象拷贝到堆上的时候会对改对象形成强引用(retain)
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

被__block 修饰的对象类型

typedef void (^MJBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [MJPerson alloc] init];
        __block __weak MJPerson *weakPerson = person;
          MJBlock block = ^{
                age = 20;
                NSLog(@"%p", weakPerson);
            };
    }
    return 0;
}

------------编译后的__main_block_impl_0----------
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
//编译器将__block 修饰的变量包装成一个对象
  __Block_byref_weakPerson_0 *weakPerson; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//__block 修饰的变量包装成一个对象 该对象拷贝到堆上的时候会对改对象形成强引用(retain)
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 void(*__Block_byref_id_object_copy)(void*, void*);
void(*__Block_byref_id_object_dispose)(void*);
//此处的 修饰符为weak 所以为弱引用 如下图
MJPerson *__weak weakPerson;
};
弱引用关系图.png

总结:
被__block 修饰的对象类型.png

__weak __unsafe_unretained 都为弱引用,__weak在指向的对象不存在的时候,会设置为nil, 而__unsafe_unretained指向的对象不存在的时候,还是会指向一块已经回收的内存地址即俗称的野指针,是不安全的,所以一般使用__weak

再来看循环引用代码

typedef void (^MJBlock) (void);

@interface MJPerson : NSObject
@property (copy, nonatomic) MJBlock block;
@property (assign, nonatomic) int age;
@end

#import "MJPerson.h"

@implementation MJPerson

@end

#import "MJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
         MJPerson *person = [MJPerson alloc] init];
         person.age = 10;
         //循环引用 如同
         person.block =  ^{
              NSLog(@"age is %d", person.age);
         }
 
    }
    return 0;
}

编译后源码不再展示 但是可以知道 block 中捕获的对象变量默认为强引用,
peson 实例对象中它的成员变量(@property (copy, nonatomic) MJBlock block)

block中存在指向 Block结构体的指针为强引用
强引用指向关系.png

如果使用__weak修饰 则可将结构体需要捕获的对象的修饰符改为弱引用

如图:
弱引用指向关系图.png

当实例对象的类对象中的成员变量不再使用block 会自动释放block对象,而block结构体中捕获的实例对象指针为弱引用,也不再继续持有实例对象,可解决循环引用
ARC解决循环引用.png

你可能感兴趣的:(iOS 底层解析-----Block (下))