OC 学习记录随笔 之 Block

总资料
全是随笔 笔记。没有规律。部分为MJ老师的课程笔记.

一、Block

  • block 本质就是一个oc对象, 它的内部存有一个isa指针
struct __block_impl {
    
    
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

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

struct __main_block_impl_0 {
    
    
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  MJPerson *__strong 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;
  }
};

在这里插入图片描述
在这里插入图片描述

1.1类型

查看方式

  • 查看isa [self class] [[self class] superclass]
  • 查看编译出来的cpp文件(编译出来的只有一个类型,runtime运行时会修改)

类型

  • __NSGlobalBlock__ :没有访问auto变量(调用copy相当于没有效果)
  • __NSStackBlock__ : 访问了auto变量(需要关闭ARC模式,不然ARC会帮代码优化调用copy
  • __NSMallocBlock____NSStackBlock__调用了copy方法

继承线路

通过取 superclass 可以得出,最终也是继承与 NSObject

  • __NSGlobalBlock__ -> __NSGlobalBlock -> NSBlock -> NSObject

1.2Copy 释放

  • block 内部访问了对象类型的auto变量时

    • block 是栈上, 将不会对 auto 变量产生强引用
  • 如果block 被拷贝到了堆上

    • 会调用block内部的copy函数. __main_block_impl_0 -> __main_block_desc_0 -> void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    • copy 会调用_Block_object_assign(void *, const void *, const int)
    • _Block_object_assign(void *, const void *, const int) 会根据auto变量的修饰符(__strong__weak__unsafe_unretained)操作,类似于retain(形成强引用、弱引用)
  • block 从堆移除

    • 会调用block 内部的dispose()函数:__main_block_impl_0 -> __main_block_desc_0 -> void (*dispose)(struct __main_block_impl_0*);
    • dispose 会调用void _Block_object_dispose(const void *, const int)
    • void _Block_object_dispose(const void *, const int) 会自动释放引用的auto变量,类似于release
copy 和 dispose 在访问对象的时候,会自动生成。
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};

1.3 __Block

  • __block 不能修饰全局变量和静态变量
  • __block 会自动将局部变量 包装成对象(包含isastruct),所以blcok会捕获这个对象的地址。 生命周期和block相同
  • 对象的__blcok 会生成 copydispose 方法 , 非对象则不会
    • __main_block_desc_0copydispose 只是拷贝结构体, 强引用
    • 实例结构体 __Block_byref_person_1 内部的 copydispose 会拷贝结构体内部的真正实例变量, 取决于修饰符 strong 或者 weak
    • 当block销毁时,__main_block_desc_0dispose 会调用,销毁struct__Block_byref_person_1 .
    • 下面 struct__Block_byref_person_1 销毁的时候又会调用 内部的dispose 销毁自己的 MJPerson *__strong person; 实例变量(如果是weak修饰的话,就应该不管了).

copy 流程如下图, 析构流程,原路回去.

请添加图片描述

引用关系图

在这里插入图片描述

具体源码如下

源代码,有两个__block
	__block int age = 10;
    __block MJPerson *person = [[MJPerson alloc] init];
    MJBlock b = ^(void) {
    
    
        age = 11;
        person.age = 12;
        NSLog(@"---%d", person.age);
    };

cpp编译后的结果

Typedef void (*MJBlock)(void);

struct __Block_byref_age_0 {
    
    
  void *__isa; //类型
  //__forwarding 指向自己的地址, 如果是copy后,就会指向堆中的地址, 如果没有copy 就指向自己
__Block_byref_age_0 *__forwarding; 
 int __flags;
 int __size;
 int age; // __forwarding 中的age才是真正的值.
};

//对象的__blcok 会生成 copy  和 dispose 方法
struct __Block_byref_person_1 {
    
    
  void *__isa;
__Block_byref_person_1 *__forwarding;  //指向自己的地址
 int __flags;
 int __size;
 //下面的两个方法啊才是 strong 和copy的选择 实现
 void (*__Block_byref_id_object_copy)(void*, void*); 
 void (*__Block_byref_id_object_dispose)(void*);
 MJPerson *__strong person;  //这里strong
};
static struct __main_block_desc_0 {
    
    
  size_t reserved;
  size_t Block_size;
  //这里copy 会统一拷贝 __Block_byref_age_0 和 __Block_byref_person_1 struct 到堆中,都是strong的引用
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
   //这里release 会统一拷贝 __Block_byref_age_0 和 __Block_byref_person_1 struct  到堆中,都是strong的引用
  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};


struct __main_block_impl_0 {
    
    
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref.  变量1  强引用
  __Block_byref_person_1 *person; // by ref 变量2   强引用 
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, __Block_byref_person_1 *_person, int flags=0) : age(_age->__forwarding), person(_person->__forwarding) {
    
    
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};


//CPP 内部的main实现
int main(int argc, const char * argv[]) {
    
    
    /* @autoreleasepool */ {
    
     __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
    
    (void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
        
         __Block_byref_person_1 person = {
    
    0,&person, 33554432, sizeof(__Block_byref_person_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, objc_msgSend)((id, SEL))objc_msgSend)(objc_getClass("MJPerson"), sel_registerName("alloc")), sel_registerName("init"))};
        
        MJBlock b = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, (__Block_byref_person_1 *)&person, 570425344));

    }
    return 0;
}


// 下面为 person 结构体的 copy 和 dispose 方法的实现
// 这里才会 真正的对 person 实例进行 strong 或者 weak 的属性. 
// 而 __main_block_desc_0 方法的 copy 和 dispose 都是强引用对象
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);
}

TODO: 后还有一个是3
_Block_object_assign((void*)&dst->person, (void*)src->person, 8/BLOCK_FIELD_IS_BYREF/);}

_Block_object_assign((char*)dst + 40, *(void * ) ((char)src + 40), 131);

1.4 解决循环引用的三个修饰符

__unsafe_unretained VS __weak

__weak typedef(self) weakSelf = self;
__unsafe_unretained typedef(self) weakSelf = self;

self.block = ^{
 	self.age = 5;
};

两者被block持有的时候,都不会产生循环引用

  • __weak: 指向的对象销毁时,会自动让指针置为 nil
  • __unsafe_unretained: 不安全,指向的对象销毁时,自己的指针存储的地址不变,会产生野指针

__block

__block Person *per = [Person new];
per.age = 44;

per.myBlock = ^{
	per.age = 55;
	per = nil;
};
per.myBlock();

如上代码,__block 也可以解决循环引用,但是必须执行一次block,并且手动在block 里面将 per 置为nil。才能打破三角循环。

引用结构如下

如果用weak修饰这里就是虚线
per
myBlcok
__Block_byref_person_1 内部的per
  1. 运行per.myBlock();
  2. block内部代码会手动将 __Block_byref_person_1 内部的per置为nil。打破一个箭头
  3. 等到block生命周期后,又会打破一个箭头。

最终结构如下:

per
myBlcok
__Block_byref_person_1 内部的per

所以能打破循环引用。但是如果不执行block的话是打破不了循环引用的,所以需慎用

1.5 strong 和 copy 修饰符的区别

@propert (nonatomic, strong) MyBlock block;
@propert (nonatomic, copy) MyBlock block;

两者在ARC模式下无区别。
因为 使用strong的时候,ARC模式下也会自动copy到堆上。
之所以推介使用copy,是因为保持MRC的习惯,或者说显示的提醒是copy到堆上去了的。

猜你喜欢

转载自blog.csdn.net/goldWave01/article/details/121445902