本文主要是介绍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函数,两个参数分别是Class
和SEL
关于objc_msgSend
内发生的事情,参见我之前的一篇博客:
- iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)
到这里,我们知道了一个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,
这篇关于Objective C block背后的黑魔法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!