Objective C block背后的黑魔法

前言

block在Objective C开发中应用非常广泛,我们知道block会捕获外部对象,也知道使用block要防止循环引用。

“知其然而不知其所以然”是一件很痛苦的事情,那么block这套机制在OC中是如何实现的呢?本文通过从C/C++到汇编层面分析block的实现原理。


Clang

clang是XCode的编译器前端,编译器前端负责语法分析,语义分析,生成中间代码(intermediate representation )。

比如当你在XCode中进行build一个.m文件的时候,实际的编译命令如下

clang -x objective-c -arch x86_64
 -fmessage-length=0 
 -fobjc-arc... 
 -Wno-missing-field-initializers ... 
 -DDEBUG=1 ... 
 -isysroot iPhoneSimulator10.1.sdk 
 -fasm-blocks ... 
 -I headers.hmap 
 -F 所需要的Framework  
 -iquote 所需要的Framework  ... 
 -c ViewController.m 
 -o ViewController.o

Objective C也可以用GCC来编译,不过那超出了本文的范畴,不做讲解。

Clang除了能够进行编译之外,还有其他一些用法。比如本文分析代码的核心命令就是这个:

clang -rewrite-objc 文件.m

通过这个命令,我们可以把Objective C的代码用C++来表示。

对于想深入理解Clang命令的同学,可以用命令忙自带的工具来查看帮助文档

man clang

或者阅读官方文档:文档地址


查看汇编代码

在XCode中,对于一个源文件,我们可以通过如下方式查看其汇编代码。这对我们分析代码深层次的实现原理非常有用,这个在后面也会遇到。


Objective C对象内存模型

为了本文讲解的更清楚,我们首先来看看一个Objective C对象的内存模型。我们首先新建一个类,内容如下

DemoClass.h

@interface DemoClass : NSObject
@property (nonatomic, copy) NSString * value;
@end

DemoClass.m

@implementation DemoClass
- (void)demoFunction{
    DemoClass * obj = [[DemoClass alloc] init];
}
@end

然后,我们用上文提到的Clang命令将DemoClass.m转成C++的表示。

clang -rewrite-objc DemoClass.m

转换完毕后当前目录会多一个DemoClass.cpp文件,这个文件很大,接近十万行。

我们先搜索这个方法名称demoFunction,以方法作为切入

static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {
    DemoClass * obj = ((DemoClass *(*)(id, SEL))(void *)objc_msgSend)((id)((DemoClass *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("DemoClass"), sel_registerName("alloc")), sel_registerName("init"));
}

可以看到,转换成C++后,一个实例方法转换为一个静态方法,这个方法的内容看起来很乱,因为有各种的类型强制转换,去掉后就比较清楚了。

static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {
    DemoClass * obj = objc_msgSend(objc_msgSend(objc_getClass("DemoClass"), sel_registerName("alloc")), sel_registerName("init"));
}

可以看到:

  • 转换后增加了两个参数:self_cmd
  • 方法的调用转换成了objc_msgSend,这是一个C函数,两个参数分别是ClassSEL

关于objc_msgSend内发生的事情,参见我之前的一篇博客:

到这里,我们知道了一个OC的实例方法具体是怎么实现的了。

那么,一个OC对象在内存中是如何存储的呢?我们在刚刚的方法的上下可以找到这个类的完整实现,

//类对应的结构体
struct DemoClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_value;
};
//demoFunction方法
static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {
    DemoClass * obj = objc_msgSend(objc_msgSend(objc_getClass("DemoClass"), sel_registerName("alloc")), sel_registerName("init"));
}
//属性value的getter方法
static NSString * _I_DemoClass_value(DemoClass * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_DemoClass$_value)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

//属性value的setter方法
static void _I_DemoClass_setValue_(DemoClass * self, SEL _cmd, NSString *value) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct DemoClass, _value), (id)value, 0, 
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值