总资料
全是随笔 笔记。没有规律。部分为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
会自动将局部变量 包装成对象(包含isa
的struct
),所以blcok会捕获这个对象的地址。 生命周期和block相同- 对象的
__blcok
会生成copy
和dispose
方法 , 非对象则不会__main_block_desc_0
的copy
和dispose
只是拷贝结构体, 强引用- 实例结构体
__Block_byref_person_1
内部的copy
和dispose
会拷贝结构体内部的真正实例变量, 取决于修饰符strong
或者weak
- 当block销毁时,
__main_block_desc_0
的dispose
会调用,销毁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。才能打破三角循环。
引用结构如下
- 运行
per.myBlock();
block
内部代码会手动将__Block_byref_person_1 内部的per
置为nil。打破一个箭头- 等到
block
生命周期后,又会打破一个箭头。
最终结构如下:
所以能打破循环引用。但是如果不执行block的话是打破不了循环引用的,所以需慎用。
1.5 strong 和 copy 修饰符的区别
@propert (nonatomic, strong) MyBlock block;
@propert (nonatomic, copy) MyBlock block;
两者在ARC
模式下无区别。
因为 使用strong
的时候,ARC
模式下也会自动copy
到堆上。
之所以推介使用copy,是因为保持MRC的习惯,或者说显示的提醒是copy到堆上去了的。