if (!doneOnce) { doneOnce = YES; launchTime = YES;
#if SUPPORT_NONPOINTER_ISA // Disable non-pointer isa under some conditions.
# if SUPPORT_INDEXED_ISA // Disable nonpointer isa if any image contains old Swift code for (EACH_HEADER) { if (hi->info()->containsSwift() && hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3) { DisableNonpointerIsa = true; if (PrintRawIsa) { _objc_inform("RAW ISA: disabling non-pointer isa because " "the app or a framework contains Swift code " "older than Swift 3.0"); } break; } } # endif
# if TARGET_OS_OSX // Disable non-pointer isa if the app is too old // (linked before OS X 10.11) if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) { DisableNonpointerIsa = true; if (PrintRawIsa) { _objc_inform("RAW ISA: disabling non-pointer isa because " "the app is too old (SDK version " SDK_FORMAT ")", FORMAT_SDK(dyld_get_program_sdk_version())); } }
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section // New apps that load old extensions may need this. for (EACH_HEADER) { if (hi->mhdr()->filetype != MH_EXECUTE) continue; unsignedlong size; if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) { DisableNonpointerIsa = true; if (PrintRawIsa) { _objc_inform("RAW ISA: disabling non-pointer isa because " "the app has a __DATA,__objc_rawisa section"); } } break; // assume only one MH_EXECUTE image } # endif
#endif
if (DisableTaggedPointers) { disableTaggedPointers(); } initializeTaggedPointerObfuscator();
if (PrintConnecting) { _objc_inform("CLASS: found %d classes during launch", totalClasses); }
// 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);
ts.log("IMAGE TIMES: first time tasks"); }
// Fix up @selector references staticsize_t UnfixedSelectors; { mutex_locker_tlock(selLock); for (EACH_HEADER) { if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle(); SEL *sels = _getObjc2SelectorRefs(hi, &count); UnfixedSelectors += count; for (i = 0; i < count; i++) { constchar *name = sel_cname(sels[i]); SEL sel = sel_registerNameNoLock(name, isBundle); if (sels[i] != sel) { sels[i] = sel; } } } }
ts.log("IMAGE TIMES: fix up selector references");
// Discover classes. Fix up unresolved future classes. Mark bundle classes. bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) { if (! mustReadClasses(hi, hasDyldRoots)) { // Image is sufficiently optimized that we need not call readClass() continue; }
for (i = 0; i < count; i++) { Class cls = (Class)classlist[I]; Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) { // Class was moved but not deleted. Currently this occurs // only when the new class resolved a future class. // Non-lazily realize the class below. resolvedFutureClasses = (Class *) realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class)); resolvedFutureClasses[resolvedFutureClassCount++] = newCls; } } }
ts.log("IMAGE TIMES: discover classes");
// Fix up remapped classes // Class list and nonlazy class list remain unremapped. // Class refs and super refs are remapped for message dispatching. if (!noClassesRemapped()) { for (EACH_HEADER) { Class *classrefs = _getObjc2ClassRefs(hi, &count); for (i = 0; i < count; i++) { remapClassRef(&classrefs[I]); } // fixme why doesn't test future1 catch the absence of this? classrefs = _getObjc2SuperRefs(hi, &count); for (i = 0; i < count; i++) { remapClassRef(&classrefs[I]); } } }
ts.log("IMAGE TIMES: remap classes");
#if SUPPORT_FIXUP // Fix up old objc_msgSend_fixup call sites for (EACH_HEADER) { message_ref_t *refs = _getObjc2MessageRefs(hi, &count); if (count == 0) continue;
if (PrintVtables) { _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch " "call sites in %s", count, hi->fname()); } for (i = 0; i < count; i++) { fixupMessageRef(refs+i); } }
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup"); #endif
// Discover protocols. Fix up protocol refs. for (EACH_HEADER) { extern objc_class OBJC_CLASS_$_Protocol; Class cls = (Class)&OBJC_CLASS_$_Protocol; ASSERT(cls); NXMapTable *protocol_map = protocols(); bool isPreoptimized = hi->hasPreoptimizedProtocols();
// Skip reading protocols if this is an image from the shared cache // and we support roots // Note, after launch we do need to walk the protocol as the protocol // in the shared cache is marked with isCanonical() and that may not // be true if some non-shared cache binary was chosen as the canonical // definition if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) { if (PrintProtocols) { _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s", hi->fname()); } continue; }
bool isBundle = hi->isBundle();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count); for (i = 0; i < count; i++) { readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle); } }
ts.log("IMAGE TIMES: discover protocols");
// Fix up @protocol references // Preoptimized images may have the right // answer already but we don't know for sure. for (EACH_HEADER) { // At launch time, we know preoptimized image refs are pointing at the // shared cache definition of a protocol. We can skip the check on // launch, but have to visit @protocol refs for shared cache images // loaded later. if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized()) continue; protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count); for (i = 0; i < count; i++) { remapProtocolRef(&protolist[I]); } }
ts.log("IMAGE TIMES: fix up @protocol references");
// Discover categories. Only do this after the initial category // attachment has been done. For categories present at startup, // discovery is deferred until the first load_images call after // the call to _dyld_objc_notify_register completes. rdar://problem/53119145 if (didInitialAttachCategories) { for (EACH_HEADER) { load_categories_nolock(hi); } }
ts.log("IMAGE TIMES: discover categories");
// Category discovery MUST BE Late to avoid potential races // when other threads call the new category code before // this thread finishes its fixups.
// +load handled by prepare_load_methods() // Realize non-lazy classes (for +load methods and static instances) - 懒加载类 -> 非懒加载类 // 懒 别人不懂我 我就不动 - 让它 提前加载 - load_images 类 // 懒加载类在什么时候? for (EACH_HEADER) { classref_tconst *classlist = _getObjc2NonlazyClassList(hi, &count); for (i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); constchar *mangledName = cls->mangledName(); constchar *LGPersonName = "LGPerson"; if (strcmp(mangledName, LGPersonName) == 0) { auto kc_ro = (constclass_ro_t *)cls->data(); printf("_getObjc2NonlazyClassList: 这个是我要研究的 %s \n",LGPersonName); } 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 } // alloc init - 类存在 完备 实例 realizeClassWithoutSwift(cls, nil); } }
ts.log("IMAGE TIMES: realize non-lazy classes"); // Realize newly-resolved future classes, in case CF manipulates them 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, nil); cls->setInstancesRequireRawIsaRecursively(false/*inherited*/); } free(resolvedFutureClasses); }
ts.log("IMAGE TIMES: realize future classes");
if (DebugNonFragileIvars) { realizeAllClasses(); }
staticvoidaddNamedClass(Class cls, constchar *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()); }
// 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. auto &set = objc::allocatedClasses.get();
ASSERT(set.find(cls) == set.end());
if (!isKnownClass(cls)) set.insert(cls); if (addMeta) addClassTableEntry(cls->ISA(), false); }
rw 表示 readWrite,即可读可写,由于其动态性,可能会往类中添加属性、方法、添加协议,在最新的2020的WWDC的对内存优化的说明Advancements in the Objective-C runtime - WWDC 2020 - Videos - Apple Developer中,提到rw,其实在rw中只有10%的类真正的更改了它们的方法,所以有了rwe,即类的额外信息。对于那些确实需要额外信息的类,可以分配rwe扩展记录中的一个,并将其滑入类中供其使用。其中rw就属于dirty memory,而 dirty memory是指在进程运行时会发生更改的内存,类结构一经使用就会变成 ditry memory,因为运行时会向它写入新数据,例如 创建一个新的方法缓存,并从类中指向它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// fixme verify class is not in an un-dlopened part of the shared cache? //读取class的data(),以及ro/rw创建 auto ro = (constclass_ro_t *)cls->data(); //读取类结构的bits属性、//ro -- clean memory,在编译时就已经确定了内存 auto isMeta = ro->flags & RO_META; //判断元类 if (ro->flags & RO_FUTURE) { // This was a future class. rw data is already allocated. rw = cls->data(); //dirty memory 进行赋值 ro = cls->data()->ro(); ASSERT(!isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { //此时将数据读取进来了,也赋值完毕了 // Normal class. Allocate writeable class data. rw = objc::zalloc<class_rw_t>(); //申请开辟zalloc -- rw rw->set_ro(ro);//rw中的ro设置为临时变量ro rw->flags = RW_REALIZED|RW_REALIZING|isMeta; cls->setData(rw);//将cls的data赋值为rw形式 }
// Realize superclass and metaclass, if they aren't already. // This needs to be done after RW_REALIZED is set above, for root classes. // This needs to be done after class index is chosen, for root metaclasses. // This assumes that none of those classes have Swift contents, // or that Swift's initializers have already been called. // fixme that assumption will be wrong if we add support // for ObjC subclasses of Swift classes. -- //递归调用realizeClassWithoutSwift完善继承链,并处理当前类的父类、元类 //递归实现 设置当前类、父类、元类的 rw,主要目的是确定继承链 (类继承链、元类继承链) //实现元类、父类 //当isa找到根元类之后,根元类的isa是指向自己的,不会返回nil从而导致死循环——remapClass中对类在表中进行查找的操作,如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归 supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil); metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil); ...
// Update superclass and metaclass in case of remapping -- class 是 双向链表结构 即父子关系都确认了 // 将父类和元类给我们的类 分别是isa和父类的对应值 cls->superclass = supercls; cls->initClassIsa(metacls);
...
// Connect this class to its superclass's subclass lists //双向链表指向关系 父类中可以找到子类 子类中也可以找到父类 //通过addSubclass把当前类放到父类的子类列表中去 if (supercls) { addSubclass(supercls, cls); } else { addRootClass(cls); }
staticvoidmethodizeClass(Class cls, Class previously) { runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass(); auto rw = cls->data(); // 初始化一个rw auto ro = rw->ro(); auto rwe = rw->ext(); ...
// Install methods and properties that the class implements itself. //将属性列表、方法列表、协议列表等贴到rw中 // 将ro中的方法列表加入到rw中 method_list_t *list = ro->baseMethods();//获取ro的baseMethods if (list) { prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));//methods进行排序 if (rwe) rwe->methods.attachLists(&list, 1);//对rwe进行处理 } // 加入属性 property_list_t *proplist = ro->baseProperties; if (rwe && proplist) { rwe->properties.attachLists(&proplist, 1); } // 加入协议 protocol_list_t *protolist = ro->baseProtocols; if (rwe && protolist) { rwe->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, @selector(initialize), (IMP)&objc_noop_imp, "", NO); }
// Attach categories. // 加入分类中的方法 if (previously) { if (isMeta) { objc::unattachedCategories.attachToClass(cls, previously, ATTACH_METACLASS); } else { // When a class relocates, categories with class methods // may be registered on the class itself rather than on // the metaclass. Tell attachToClass to look for those. objc::unattachedCategories.attachToClass(cls, previously, ATTACH_CLASS_AND_METACLASS); } } objc::unattachedCategories.attachToClass(cls, cls, isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
// fixme lock less in attachMethodLists ? // dyld3 may have already uniqued, but not sorted, the list if (!mlist->isUniqued()) { mutex_locker_tlock(selLock); // Unique selectors in list. for (auto& meth : *mlist) { constchar *name = sel_cname(meth.name); meth.name = sel_registerNameNoLock(name, bundleCopy); } }
// Sort by selector address.根据sel地址排序 if (sort) { method_t::SortBySELAddress sorter; std::stable_sort(mlist->begin(), mlist->end(), sorter); } // Mark method list as uniqued and sorted mlist->setFixedUp(); }
if (strcmp(mangledName, LGPersonName) == 0) { bool kc_isMeta = cls->isMetaClass(); auto kc_rw = cls->data(); auto kc_ro = kc_rw->ro(); if (!kc_isMeta) { printf("%s: 这个是我要研究的 %s \n",__func__,LGPersonName); } } auto &map = get(); auto it = map.find(previously);//找到一个分类进来一次,即一个个加载分类,不要混乱
/* * Only a few classes have more than 64 categories during launch. * This uses a little stack, and avoids malloc. * * Categories must be added in the proper order, which is back * to front. To do that with the chunking, we iterate cats_list * from front to back, build up the local buffers backwards, * and call attachLists on the chunks. attachLists prepends the * lists, so the final result is in the expected order. */ constexpruint32_t ATTACH_BUFSIZ = 64; method_list_t *mlists[ATTACH_BUFSIZ]; property_list_t *proplists[ATTACH_BUFSIZ]; protocol_list_t *protolists[ATTACH_BUFSIZ];