ios 魔法block,是我最喜欢的没有之一,好用又好玩。
对于它的实现方式一直是模棱两可,从来不敢说完全懂了。
这两天不知道怎么回事,脑袋里冒出来一些想法,想把block再深究一下,
看看他到底有什么不一样?
如果有什么不对的对方还望提醒,在此谢过。
1.先看看block的声明会发生什么
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^NameBlock)(NSString *name);
@interface TestBlock : NSObject
@property(nonatomic,copy)NameBlock block;
- (void)run:(NameBlock)block;
@end
NS_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
//function void 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);
//_block setter方法 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 = { 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 = { 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
mov x1, x0 //_cmd = self x0 mov 从x0取值到x1
ldr x0, [x17] //self = block object 从[x17]取值impl到x0
ldr x16, [x0, #16] // tail call block->invoke 从x0<<16找impl.FuncPtr到x16
br x16 //调用 x16
更新中!!!!!!!!!
网友评论