Object-C作为一门动态语言,它的动态特性是其最基本也是最重要的特性,之所以重要是因为其很多功能及扩展扩展都是基于该特性去完成的。比如:autorelease是如何是实现的,category是如何是实现的,等等这些问题在objc的源代码中我们都能找到答案…
出发准备
在Xcode中对project进行build的过程就是一个编译-链接-生成可执行文件的过程,在Mac OS X系统中每一个可执行文件都是一个Mach-O文件格式。有关Mach-O文件的内容请看这篇文章,当我们运行一个可执行文件时,即在程序运行时会发生什么呢?
在Xcode中随便创建一个simple viewcontroller工程,添加符号断点:

在Symbol一栏中输入_objc_init
来添加断点,

成功添加断点后运行程序,程序停在我们的断点处:

熟悉C语言的同学都知道,C语言的程序入口函数是main(),但是由于OC相交于C增加了动态的特性,所以对于OC程序而言,需要在main()函数之前先运行起一个动态系统,即libobjc.A.dylib库的执行,_objc_init
函数就是libobjc.A.dylib的程序入口,然后的执行顺序是_os_objct_init
->_libdispatch_init
->libSystem_initializer
->_dyld_start
,函数_dyld_stat
记录着我们程序的main.m中的main()
函数的地址,最终会调起我们的代码去执行。
开始
从objc4-646工程可以看到,最终该工程生成的就是libobjc.A.dylib库,这个库就是objec-C runtime的整体实现,下面我们就从_objc_init
函数入手看看这个库到底做了什么?_objc_init
的实现,函数位于/runtime/objc-os.mm
:
1 | void _objc_init(void) |
environ_init()
:检查scheme中环境变量设置情况,以便在控制台输出相应的内容。所有环境变量的宏定义在/runtime/objc-evn.h
中
主要的几个变量及其含义如下:
OBJC_PRINT_IMAGES:打印加载的映像和库
OBJC_PRINT_CLASS_SETUP:打印类和分类的处理过程
具体操作步骤:首先Perduct->Scheme->Edit Scheme

点击 + 号添加环境变量:

最后输入上面的环境变量,值设为YES

运行程序在控制台就会输出环境变量相对应的内容:

tls_init()
:创建线程共享。
1 | typedef pthread_key_t tls_key_t; |
lock_init()
:初始化一个读-写锁和递归互斥锁,由于在初始化过程中好多地方都要注意数据竞争,需要用到锁来处理。exception_init()
:初始化libobjc的异常处理系统。map_images()
:将类映射到内存中。具体内容下节见。load_images()
:调用类的[+ load]
方法。具体内容写完map_images()
再说。
map_images()
我们看看map_images()
做了那些处理,跟进代码我们看到最终来到了map_images_nolock()
这个函数中,代码如下:
1 | const char * |
addHeader()
最终会调用appendHeader()
,它会将mhdr通过appendHeader()
函数添加到链表中,并由FirstHeader
,LastHeader
和HeaderCount
去维护。
1 | void appendHeader(header_info *hi) |
_getObjcSelectorRefs()
会获取__OBJC
segment下section名称为__message_refs
的数据,并从中得出selector的数量:
1 | SEL *__getObjcSelectorRefs(const header_ *hi, size_t *outCount) |
sel_init()
:记录selctor数量存储在SelrefCount
中;初始化selector列表(NSObject的基类方法)并使用__sel_registerName()方法注册selectorsarr_init()
:初始化AutoreleasePoolPage类实现autorelease机制;初始化SizeTable类,用于对__weak
对象的维护。
AutoreleasePoolPage是实现autorelease的类,具体的autorelease在什么时候去release对象以及何时去release等问题,请看这篇文章_read_images()
:这里根据设置环境变量的输出,走的是/runtime/objc-runtime-new.mm
中的_read_images()
方法,具体详情下一节见。
read_images()
/runtime/objc-runtime-new.mm
中的_read_images()
方法的代码为:
1 | /*********************************************************************** |
NXMapTable
:从上面的代码中可以看出Objcet-C中用于存储类或者协议的结构就是NXMapTable
,这是一种hash结构体,有关该结构体的介绍请参阅这篇文章。NSMapTable
定义在/runtime/hashtable2.h
中。_getObjc2ProtocolList()
:对协议进行处理relizedClass
:实现类addUnattachedCategoryForClass()
:这个方法我们可以着重看一下,它就是Object-C中将分类加到类的方法列表的核心方法,我们看看这个方法都做了什么:
1 | static void addUnattachedCategoryForClass(category_t *cat, Class cls, |
从上面代码我们可以看出,它是将从Mach-O文件中读取到的分类方法lsit,插入了一个叫category_map得NXMapTable中,该list对应的key就是分类将要添加到的类的名称,下一步的remethodizeClass()
方法最终会读取category_map中的list,并通过attachCategoryMethods
->attachMethodLists
将类的分类的转换成类的方法,更详细的添加过程请参阅这篇文章。
load_images()
最后我们再看一下load_image()
函数都做了些什么:
1 | const char * |
通过上面的代码我们知道,load_images()
围绕的就是调用类或分类中的[+ load]
方法。
由于能力和经验有限,有些地方可能分析的不够深入,更详细的分析以后补充吧!