类的加载
类的加载
1.0 objc_init分析
通过对dyld动态链接流程的分析最后会来到objc_init
来
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
1.0.1 environ_init 初始化环境变量
void environ_init(void)
{
...
//代码太长省略
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
}
...
}
这里是经过循环打印所有的环境变量,我们直接拷贝这段代码,到判断条件外边.直接打印下环境变量
objc[4791]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[4791]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[4791]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[4791]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[4791]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[4791]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[4791]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[4791]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[4791]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[4791]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[4791]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
objc[4791]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
objc[4791]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
objc[4791]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
objc[4791]: OBJC_PRINT_EXCEPTIONS: log exception handling
objc[4791]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
objc[4791]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
objc[4791]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
objc[4791]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
objc[4791]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
objc[4791]: OBJC_PRINT_CUSTOM_RR: log classes with un-optimized custom retain/release methods
objc[4791]: OBJC_PRINT_CUSTOM_AWZ: log classes with un-optimized custom allocWithZone methods
objc[4791]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
objc[4791]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc[4791]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
objc[4791]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
objc[4791]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
objc[4791]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
objc[4791]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
objc[4791]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
objc[4791]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
objc[4791]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
objc[4791]: OBJC_DISABLE_VTABLES: disable vtable dispatch
objc[4791]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
objc[4791]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
objc[4791]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[4791]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[4791]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
可能好多码友这样干过,比如在Edit Scheme
里边添加环境变量如图
看 一个例子
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LGPerson *object = [LGPerson alloc];
NSLog(@"Hello, World! %@",object);
// [object saySomething];
}
return 0;
}
进行lldb打印
(lldb) x/4xg object
0x101230830: 0x001d800100001191 0x0000000000000000
0x101230840: 0x646e6946534e5b2d 0x536e726574746150
(lldb) p/x LGPerson.class
(Class) $9 = 0x0000000100001190 LGPerson
(lldb) p/x 0x001d800100001191 & 0x00007ffffffffff8
(long) $10 = 0x0000000100001190
(lldb)
打印Person
类,地址为0x0000000100001190
拿到isa:0x001d800100001191
与mask:0x00007ffffffffff8
进行与运算 得出 0x0000000100001190
我们发现是一样的,因为现在的isa
指针是没有优化的,所以要进行&
运算
那么现在加上OBJC_DISABLE_NONPOINTER_ISA
这个环境变量并设置为YES
,表示对isa
进行优化,再来看
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100001190 LGPerson
(lldb) x/4xg object
0x101636c70: 0x0000000100001190 0x0000000000000000
0x101636c80: 0x72626956534e5b2d 0x74696c7053746e61
不用进行&
运算直接就是相等的
在来看一个例子OBJC_PRINT_LOAD_METHODS
环境变量
objc[5000]: LOAD: class '__IncompleteProtocol' scheduled for +load
objc[5000]: LOAD: class 'Protocol' scheduled for +load
objc[5000]: LOAD: class '__NSUnrecognizedTaggedPointer' scheduled for +load
objc[5000]: LOAD: +[__IncompleteProtocol load]
objc[5000]: LOAD: +[Protocol load]
objc[5000]: LOAD: +[__NSUnrecognizedTaggedPointer load]
objc[5000]: LOAD: category 'NSObject(NSObject)' scheduled for +load
objc[5000]: LOAD: +[NSObject(NSObject) load]
objc[5000]: LOAD: category 'NSObject(NSObject)' scheduled for +load
objc[5000]: LOAD: +[NSObject(NSObject) load]
objc[5000]: LOAD: category 'CIFilter(Interposer)' scheduled for +load
objc[5000]: LOAD: +[CIFilter(Interposer) load]
objc[5000]: LOAD: class 'NSApplication' scheduled for +load
objc[5000]: LOAD: class 'NSBinder' scheduled for +load
objc[5000]: LOAD: class 'NSColorSpaceColor' scheduled for +load
objc[5000]: LOAD: class 'NSNextStepFrame' scheduled for +load
objc[5000]: LOAD: category 'NSColor(NSUIKitSupport)' scheduled for +load
objc[5000]: LOAD: +[NSApplication load]
objc[5000]: LOAD: +[NSBinder load]
objc[5000]: LOAD: +[NSColorSpaceColor load]
objc[5000]: LOAD: +[NSNextStepFrame load]
objc[5000]: LOAD: +[NSColor(NSUIKitSupport) load]
objc[5000]: LOAD: category 'NSError(FPAdditions)' scheduled for +load
objc[5000]: LOAD: +[NSError(FPAdditions) load]
objc[5000]: LOAD: class '_DKEventQuery' scheduled for +load
objc[5000]: LOAD: +[_DKEventQuery load]
objc[5000]: LOAD: class 'LGPerson' scheduled for +load
objc[5000]: LOAD: +[LGPerson load]
将所有实现的load方法打印出来.load方法太多将影响启动速度,利用这个环境变量可以对load方法进行查看
问题: 我没有源码怎么办
那么来一个装逼的命令export OBJC_HELP=1
cd到当前项目根目录,然后终端输入该命令然后回车键,可能有些人输入之后没有反应,可以在随便输入一个简单的命令比如ls
,然后回车OK,完美
1.0.2 tls_init()
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
主要是线程的key的绑定
1.0.3 static_init()
/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors,
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
注释告诉我们,这里会调用C++的静态构造函数
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LGPerson *object = [LGPerson alloc];
NSLog(@"Hello, World! %@",object);
// [object saySomething];
}
return 0;
}
在inits打断点,然后运行项目,发现count现在为11.我们现在构造几个C++函数,再次运行
struct LGTeacher{
LGTeacher(){
printf("LGTeacher 初始化 \n");
}
~LGTeacher(){
printf("LGTeacher 析构了");
}
};
struct LGFunc{
LGFunc(){
printf("LGFunc 初始化 \n");
}
~LGFunc(){
printf("LGFunc 析构了");
}
};
发现count还是11.显然,这里不是调用我们自己实现的C++构造函数,而是系统的一些C++.
- libc calls _objc_init() before dyld would call our static constructors, 这句注释也告诉我们原因,
_objc_init
方法在dyld调用我们自己的静态方法之前.
1.0.4 lock_init
void lock_init(void)
{
}
啥也没有,可能是让我们重写,也可能是预留接口,也可能是没有开源.在这装逼,苹果这一天天的.
1.0.5 exception_init
异常初始化.监听回调异常,
static void _objc_terminate(void)
{
if (PrintExceptions) {
_objc_inform("EXCEPTIONS: terminating");
}
if (! __cxa_current_exception_type()) {
// No current exception.
(*old_terminate)();
}
else {
// There is a current exception. Check if it's an objc exception.
@try {
__cxa_rethrow();
} @catch (id e) {
// It's an objc object. Call Foundation's handler, if any.
(*uncaught_handler)((id)e);
(*old_terminate)();
} @catch (...) {
// It's not an objc object. Continue to C++ terminate.
(*old_terminate)();
}
}
}
只要有异常,就会进入这里
1.0.6 map_images
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
//...省略代码,这些代码都是一些计算hCount,以及一些打印
if (hCount > 0) {
//读取镜像,这里是重要代码
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
//简化后的代码
// 1:第一次进来 - 开始创建表
// gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
// allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
if (!doneOnce) {
doneOnce = YES;
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
// 2:类处理
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
}
// 3: 方法编号处理
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
// 4: 协议处理
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
NXMapTable *protocol_map = protocols();
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// 5: 非懒加载类处理
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
addClassTableEntry(cls);
realizeClassWithoutSwift(cls);
}
// 6: 待处理的类
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls);
cls->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 7:分类处理
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
}
}
}
// 2:类处理
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
}
此时cls还不是名字,只是一个地址,因为还没有处理,还没有读取所以还读不出类只是一个类的一个地址.
1.0.6.1 readClass
- readClass做了些什么呢
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized){
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
有些人说这里是对rw,ro等一系列进行处理,其实不是的,因为这里打个断点,根本是不进来的.这里最重要的是下边两个函数
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
assert(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// assert(!cls->isRealized());
}
将读取到的类加入到gdb_objc_realized_classes
总表中
static void addClassTableEntry(Class cls, bool addMeta = true) {
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
assert(!NXHashMember(allocatedClasses, cls));
if (!isKnownClass(cls))
NXHashInsert(allocatedClasses, cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
将读取到的类和元类加入到allocatedClasses
,因为这个时候已经分配到了内存
// Realize non-lazy classes (for +load methods and static instances)
// 实现非懒加载的类,对于load方法和静态实例变量
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
// printf("non-lazy Class:%s\n",cls->mangledName());
if (!cls) continue;
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
// 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
realizeClassWithoutSwift(cls);
}
}
1.0.6.2 realizeClassWithoutSwift
realizeClassWithoutSwift
开始对类进行处理
static Class realizeClassWithoutSwift(Class cls)
{
if (!cls) return nil;
if (cls->isRealized()) return cls;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
cls->superclass = supercls;
cls->initClassIsa(metacls);
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls);
}
先看一下else条件里边,对rw的ro进行赋值,这时候rw还没有进行处理,所以rw里的methods,propertys等属性还没有值.
继续看
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
为了保证superClass和isa的完整性,递归对父类和元类进行处理,而递归的出口就是if (!cls) return nil;
找父类最终会找到NSObject
,而NSObject
的父类就是nil
.然后你将值复制给class
if (supercls) {
addSubclass(supercls, cls);
}
然后判断是否为父类,如果是执行addSubclass
,完善继承链接关系
1.0.6.3 methodizeClass
然后调用methodizeClass
,对rw进行处理
static void methodizeClass(Class cls)
{
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
}
取到ro.然后将baseMethod
,properties
,protolist
,以及分类等等一系列贴到rw里边去,为什么要存两份呢
ro
: readonly
rw
: read write
从名字就可以看出一个可读可写,一个只读,目的就是为了防止被玩坏,所以另存一份.
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;//10
uint32_t newCount = oldCount + addedCount;//4
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;// 10+4
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
分为多对多,一对多,一对一等几种情况
多对多: 拿到oldCount
和addedCount
,进行扩容,然后将源list移到新的list的末尾,然后将新的list拷贝到前边
一对一: 直接在加到源list的首尾
一对多 : 先扩容然后将新的list 拷贝到前边
总结:
- _read_images ,
1.0 创建表,
1.1 读取所有类
1.2 获取所有类的引用
1.3 处理方法编号
1.4 修复旧的objc_msgSend_fixup
1.5 处理协议
1.6 重映射协议的引用
1.7 实现非懒加载类
1.8 处理分类 - readClass :将类插入到表中
- realizeClassWithoutSwift :对类的ro,以及父类等进行处理
- methodizeClass : 对rw进行处理