==app启动到Runtime==
例子:
#import <Foundation/Foundation.h>
@interface HelloWorld : NSObject
- (void) sayHello;
@end
@implementation HelloWorld
+ (void) load {
NSLog(@"Hello World load");
}
- (instancetype) init {
if(self = [super init]) {
NSLog(@"Hello World init");
return self;
} else
return nil;
}
- (void) sayHello {
NSLog(@"Hello World");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
HelloWorld* helloworld = [HelloWorld new];
[helloworld sayHello];
}
return 0;
}
编译完成后,这个可执行文件,有一个头部,用于表明文件信息:
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 X86_64 ALL LIB64 EXECUTE 18 2496 NOUNDEFS DYLDLINK TWOLEVEL PIE
当你执行它,该二进制文件会被读取,操作系统可执行程序加载器会加载该文件,读取该头部,判断是否是一个合法的二进制可执行文件,如果可以执行,则开始文件的 parse (解析)过程,该过程提取 LC (Load Command) 信息。这里会找到应用程序入口信息 LC_MAIN,动态连接器加载器 LC_LOAD_DYLINKER 信息等,如果找到合法的动态连接器,则调用它,将控制权交给它,这时默认的 dyld (动态链接器)会再对二进制文件进行 parse,找到 LC_LOAD_DYLIB ,这里可能有多个。
对于一个最简单的 Cocoa App,至少会有 :
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24)
/usr/lib/libobjc.A.dylib (offset 24) (ObjC runtime)
/usr/lib/libSystem.B.dylib (offset 24) (C runtime)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24)
这时 dyld 会加载或映射这些库到地址空间中来,解析符号,初始化库,在初始化的过程中,库可以向 dyld 注册回调函数,runtime 这时即可获取文件中诸如 __objc_classlist __objc_nlclslist __objc_methname 等等信息,用于构建各类关系,为 OC 特性作支撑。
最后,dyld 会找到 LC_MAIN 入口压入初始化参数等,跳转将控制权交与程序本身,这时开始运行。
运行过程中,dyld 负责动态解析符号,比如 [helloworld sayHello] 该调用实际在编译时被替换为 objc_msgSend 然而 objc_msgSend 又在 runtime 里,这样,runtime 也就会时时存在,所有的方法调用,OC 特性的使用都会到 runtime 里。
==Category 在编译过后,是在什么时机与原有的类合并到一起的?==
- 程序启动后,通过编译之后,Runtime 会进行初始化,调用 _objc_init。
- 然后会 map_images。
- 接下来调用 map_images_nolock。
- 再然后就是 read_images,这个方法会读取所有的类的相关信息。
- 最后是调用 reMethodizeClass:,这个方法是重新方法化的意思。
- 在 reMethodizeClass: 方法内部会调用 attachCategories: ,这个方法会传入 Class 和 Category ,会将方法列表,协议列表等与原有的类合并。最后加入到 class_rw_t 结构体
网友评论