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()会获取__OBJCsegment下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]方法。
由于能力和经验有限,有些地方可能分析的不够深入,更详细的分析以后补充吧!