ios|ios block的完全理解循环引用
ios 魔法block,是我最喜欢的没有之一,好用又好玩。
对于它的实现方式一直是模棱两可,从来不敢说完全懂了。
这两天不知道怎么回事,脑袋里冒出来一些想法,想把block再深究一下,
看看他到底有什么不一样?
如果有什么不对的对方还望提醒,在此谢过。
1.先看看block的声明会发生什么
#import NS_ASSUME_NONNULL_BEGINtypedef void(^NameBlock)(NSString *name);
@interface TestBlock : NSObject
@property(nonatomic,copy)NameBlock block;
- (void)run:(NameBlock)block;
@endNS_ASSUME_NONNULL_END
#import "TestBlock.h"@implementation TestBlock- (void)run:(NameBlock)block{NSLog(@"nameBlock");
}
@end
上面的是OC的代码,很简单声明了一个block,然后run方法传参是block;
//__OFFSETOFIVAR__字面理解 TYPE的变量的地址偏移量,用来寻找变量属性的。
//#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
//NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000g//n_T_TestBlock_555780_mi_0 __attribute__ 看着灰常懵逼,但是不要怕。
//多打印几个字符串看看,只是声明静态区字符串。
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_555780_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"nameBlock",9};
//一个属性 _block 全局变量声明,之后会在ivar_list
extern "C" unsigned long OBJC_IVAR_$_TestBlock$_block;
//类TestBlock 的IMP 的list表
struct TestBlock_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NameBlock_Nonnull _block;
};
// @property(nonatomic,copy)NameBlock block;
// - (void)run:(NameBlock)block;
/* @end */#pragma clang assume_nonnull end// @implementation TestBlock//functionvoid run:(){}方法实现
static void _I_TestBlock_run_(TestBlock * self, SEL _cmd, NameBlock_Nonnull block) {//打印字符串string
NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_555780_mi_0);
}//_block 的getter方法 返回的是一个偏移指针的指针
static void(* _I_TestBlock_block(TestBlock * self, SEL _cmd) )(NSString * _Nonnull){ return (*(NameBlock_Nonnull *)((char *)self + OBJC_IVAR_$_TestBlock$_block));
}//一个声明 下面的方法会用到
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
//_blocksetter方法objc_setProperty参数_OFFSETOFIVAR__(struct TestBlock, _block) 表示_block的偏移量,(id)block就是传递的block
static void _I_TestBlock_setBlock_(TestBlock * self, SEL _cmd, NameBlock_Nonnull block) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct TestBlock, _block), (id)block, 0, 1);
}
// @end
block声明的setter 和getter方法,是单独c 类实现的。
2.1 首先看看在block中没有引用self的情况
- (void)run:(NameBlock)block{
NSLog(@"nameBlock");
NameBlock ablock = ^(NSString * _Nonnull name) {
NSLog(@"%@",name);
};
ablock(@"123");
}//编译c++代码
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __TestBlock__run__block_impl_0 {
struct __block_impl impl;
struct __TestBlock__run__block_desc_0* Desc;
__TestBlock__run__block_impl_0(void *fp, struct __TestBlock__run__block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __TestBlock__run__block_func_0(struct __TestBlock__run__block_impl_0 *__cself, NSString * _Nonnull name) {NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_2fd651_mi_4,name);
}static struct __TestBlock__run__block_desc_0 {
size_t reserved;
size_t Block_size;
} __TestBlock__run__block_desc_0_DATA = https://www.it610.com/article/{ 0, sizeof(struct __TestBlock__run__block_impl_0)};
2.2 在看看在block中引用self的情况 我会写出和上边不一样的地方
- (void)run:(NameBlock)block{NSLog(@"nameBlock");
self.block = block;
self.block = ^(NSString * _Nonnull name) {
NSLog(@"%@",name);
self.name = name;
//这里我们使用了self造成的循环引用
};
self.block(@"123");
}struct __TestBlock__run__block_impl_0 {
struct __block_impl impl;
struct __TestBlock__run__block_desc_0* Desc;
TestBlock *self;
//引用self 之后,多出来的。强引用self, self无法完全释放
__TestBlock__run__block_impl_0(void *fp, struct __TestBlock__run__block_desc_0 *desc, TestBlock *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __TestBlock__run__block_func_0(struct __TestBlock__run__block_impl_0 *__cself, NSString * _Nonnull name) {
TestBlock *self = __cself->self;
// bound by copy//引用self 之后,多出来的。NSLog((NSString *)&__NSConstantStringImpl__var_folders_pr_mqg77fcj5hd0vp7mmx1lnf6h0000gn_T_TestBlock_e38d8e_mi_2,name);
((void (*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)self, sel_registerName("setName:"), (NSString * _Nonnull)name);
}//引用self 之后,多出来的。copy方法
//之前声明的函数一值没有使用,引用self 之后会使用
//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);
//多出的两个函数,assign 和 dispose
static void __TestBlock__run__block_copy_0(struct __TestBlock__run__block_impl_0*dst, struct __TestBlock__run__block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}//引用self 之后,多出来的。dispose方法
static void __TestBlock__run__block_dispose_0(struct __TestBlock__run__block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);
}static struct __TestBlock__run__block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __TestBlock__run__block_impl_0*, struct __TestBlock__run__block_impl_0*);
//引用self 之后,多出来的。
void (*dispose)(struct __TestBlock__run__block_impl_0*);
//引用self 之后,多出来的。
} __TestBlock__run__block_desc_0_DATA = https://www.it610.com/article/{ 0, sizeof(struct __TestBlock__run__block_impl_0), __TestBlock__run__block_copy_0, __TestBlock__run__block_dispose_0};
//引用self 之后,多出来的两个参数。 __TestBlock__run__block_copy_0, __TestBlock__run__block_dispose_0
下面的代码是runtime开源文件a1a2-blocktramps-arm64.s
里面的一段代码:(也可以看看其他blcok文件,只是针对平台不同而已)
//注释写的很清楚,我们再来仔细看看
/*
x0== self
x17 == address of called trampoline's data (1 page before its code)
lr== original return address
*/
//impl.isa = &_NSConcreteStackBlock
movx1, x0//_cmd = selfx0 mov 从x0取值到x1
ldrx0, [x17]//self = block object 从[x17]取值impl到x0
ldrx16, [x0, #16]// tail call block->invoke 从x0<<16找impl.FuncPtr到x16
brx16//调用 x16
【ios|ios block的完全理解循环引用】更新中!!!!!!!!!
推荐阅读
- 学历提升的重要性!必读篇!
- 咣咣咣的ScalersTalk第四轮新概念朗读持续力训练Day107|咣咣咣的ScalersTalk第四轮新概念朗读持续力训练Day107 20190122
- 弑母女孩写给被杀母亲的一封信(妈妈,最痛苦的那个人真的不是你)
- 抱怨的后果
- 我的世界修仙记——第四章,凌霄阁
- 被女儿鄙视的爸爸,最深沉的爱
- 雨打芭蕉
- 静!
- 年兽的故事
- ubuntu出现"/dev/disk/by-uuid/xxxxxxxxx does not exist. Dropping to a shell "的恢复之路