The context pointer in the addObserver:forKeyPath:options:context: message contains arbitrary data that will be passed back to the observer in the corresponding change notifications. You may specify NULL and rely entirely on the key path string to determine the origin of a change notification, but this approach may cause problems for an object whose superclass is also observing the same key path for different reasons.
A safer and more extensible approach is to use the context to ensure notifications you receive are destined for your observer and not a superclass.
When removing an observer, keep several points in mind:
Asking to be removed as an observer if not already registered as one results in an NSRangeException. You either call removeObserver:forKeyPath:context: exactly once for the corresponding call to addObserver:forKeyPath:options:context:, or if that is not feasible in your app, place the removeObserver:forKeyPath:context: call inside a try/catch block to process the potential exception.
An observer does not automatically remove itself when deallocated. The observed object continues to send notifications, oblivious to the state of the observer. However, a change notification, like any other message, sent to a released object, triggers a memory access exception. You therefore ensure that observers remove themselves before disappearing from memory.
The protocol offers no way to ask an object if it is an observer or being observed. Construct your code to avoid release related errors. A typical pattern is to register as an observer during the observer’s initialization (for example in init or viewDidLoad) and unregister during deallocation (usually in dealloc), ensuring properly paired and ordered add and remove messages, and that the observer is unregistered before it is freed from memory.
Automatic key-value observing is implemented using a technique called isa-swizzling.
The isa pointer, as the name suggests, points to the object’s class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.
You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.
// objc_msgSendSuper2() takes the current search class, not its superclass. OBJC_EXPORT id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...) OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0);
其实看objc_msgSendSuper2的注释就可以看出来,方法查找的是本类而不是它的父类。
继续看,super是方法的第一个参数,也就是objc_super *它的结构是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
structobjc_super { /// Specifies an instance of a class. __unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message. #if !defined(__cplusplus) && !__OBJC2__ /* For compatibility with old objc-runtime.h header */ ///old结构,我们可以忽略 !__OBJC2__使用 __unsafe_unretained _Nonnull Class class; #else __unsafe_unretained _Nonnull Class super_class; #endif /* super_class is the first class to search */ };
abstractclassBindingBase{ /// 省略代码 /// However, multiple window support is not yet implemented, so currently this /// provides access to the one and only window. // TODO(gspencergoog): remove the preceding note once multi-window support is // active. // 唯一的window ui.SingletonFlutterWindow getwindow => ui.window; /// 每一个BindingBase类定义行为 都有一个 platformDispatcher 作为回调(handlers) ui.PlatformDispatcher get platformDispatcher => ui.PlatformDispatcher.instance; /// 初始化实例 void initInstances() { assert(!_debugInitialized); assert(() { _debugInitialized = true; returntrue; }()); } /// The current [WidgetsBinding], if one has been created. /// ensureInitialized方法返回的实例 /// If you need the binding to be constructed before calling [runApp], /// you can ensure a Widget binding has been constructed by calling the /// `WidgetsFlutterBinding.ensureInitialized()` function. static WidgetsBinding? get instance => _instance; static WidgetsBinding? _instance; /// 注册 service extensions 初始化之后调用 void initServiceExtensions() { ///省略代码 } }
/// The current [RendererBinding], if one has been created. static RendererBinding? get instance => _instance; static RendererBinding? _instance; ///省略代码 }
void ensureVisualUpdate() { switch (schedulerPhase) { case SchedulerPhase.idle: case SchedulerPhase.postFrameCallbacks: scheduleFrame(); return; case SchedulerPhase.transientCallbacks: case SchedulerPhase.midFrameMicrotasks: case SchedulerPhase.persistentCallbacks: return; } }
它主要是调用新的帧的调度管理。它会调用scheduleFrame方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
void scheduleFrame() { if (_hasScheduledFrame || !framesEnabled) return; assert(() { if (debugPrintScheduleFrameStacks) debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.'); returntrue; }()); ///给window设置回调 ensureFrameCallbacksRegistered(); ///调度更新 window.scheduleFrame(); _hasScheduledFrame = true; }
void scheduleWarmUpFrame() { if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) return; _warmUpFrame = true; Timeline.startSync('Warm-up frame'); finalbool hadScheduledFrame = _hasScheduledFrame; // We use timers here to ensure that microtasks flush in between. Timer.run(() { assert(_warmUpFrame); handleBeginFrame(null); }); Timer.run(() { assert(_warmUpFrame); handleDrawFrame(); // We call resetEpoch after this frame so that, in the hot reload case, // the very next frame pretends to have occurred immediately after this // warm-up frame. The warm-up frame's timestamp will typically be far in // the past (the time of the last real frame), so if we didn't reset the // epoch we would see a sudden jump from the old time in the warm-up frame // to the new time in the "real" frame. The biggest problem with this is // that implicit animations end up being triggered at the old time and // then skipping every frame and finishing in the new time. resetEpoch(); _warmUpFrame = false; if (hadScheduledFrame) scheduleFrame(); }); // Lock events so touch events etc don't insert themselves until the // scheduled frame has finished. lockEvents(() async { await endOfFrame; Timeline.finishSync(); }); }
void _object_set_associative_reference(id object, constvoid *key, id value, uintptr_t policy) { // This code used to work when nil was passed for object and key. Some code // probably relies on that to not crash. Check and handle it explicitly. // rdar://problem/44094390 if (!object && !value) return;
if (object->getIsa()->forbidsAssociatedObjects()) _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object)); ///将object封装一下 类型为DisguisedPtr DisguisedPtr<objc_object> disguised{(objc_object *)object}; ///包装policy value ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock. //根据策略类型(strong、weak等)进行处理 association.acquireValue();
if (value) { //返回的结果是一个类对 auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{}); if (refs_result.second) { /* it's the first association we make */ isFirstAssociation = true; }
/* establish or replace the association 建立或者替换关联*/ auto &refs = refs_result.first->second;///得到一个空的桶,找到引用对象类型,即第一个元素的second值 auto result = refs.try_emplace(key, std::move(association));//查找当前的key是否有association关联对象 if (!result.second) {///如果结果不存在 association.swap(result.first->second); } } else {//如果传的是空值,则移除关联,相当于移除 auto refs_it = associations.find(disguised); if (refs_it != associations.end()) { auto &refs = refs_it->second; auto it = refs.find(key); if (it != refs.end()) { association.swap(it->second); refs.erase(it); if (refs.size() == 0) { associations.erase(refs_it);
} } } } } // Call setHasAssociatedObjects outside the lock, since this // will call the object's _noteAssociatedObjects method if it // has one, and this may trigger +initialize which might do // arbitrary stuff, including setting more associated objects. if (isFirstAssociation) object->setHasAssociatedObjects(); // release the old value (outside of the lock).释放老的关联值 association.releaseHeldValue(); }
if (NumBuckets == 0) { FoundBucket = nullptr; returnfalse; } // FoundTombstone - Keep track of whether we find a tombstone while probing. const BucketT *FoundTombstone = nullptr; const KeyT EmptyKey = getEmptyKey(); const KeyT TombstoneKey = getTombstoneKey(); assert(!KeyInfoT::isEqual(Val, EmptyKey) && !KeyInfoT::isEqual(Val, TombstoneKey) && "Empty/Tombstone value shouldn't be inserted into map!"); ///通过哈希函数得到BucketNo unsigned BucketNo = getHashValue(Val) & (NumBuckets-1); unsigned ProbeAmt = 1; while (true) {//与catche_t查找imp类似,通过哈希查找 const BucketT *ThisBucket = BucketsPtr + BucketNo; // Found Val's bucket? If so, return it. 如果找到直接返回 true if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) { FoundBucket = ThisBucket; returntrue; } // If we found an empty bucket, the key doesn't exist in the set. // Insert it and return the default value. //如果是一个空桶 说明key不在集合中,将key插入 返回false if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) { // If we've already seen a tombstone while probing, fill it in instead // of the empty bucket we eventually probed to. FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket; returnfalse; }
// If this is a tombstone, remember it. If Val ends up not in the map, we // prefer to return it than something that would require more probing. // Ditto for zero values. // 以上条件都不满足 BucketNo调整进行平移、再哈希继续查找 if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) && !FoundTombstone) FoundTombstone = ThisBucket; // Remember the first tombstone found. if (ValueInfoT::isPurgeable(ThisBucket->getSecond()) && !FoundTombstone) FoundTombstone = ThisBucket;
// Otherwise, it's a hash collision or a tombstone, continue quadratic // probing. if (ProbeAmt > NumBuckets) { FatalCorruptHashTables(BucketsPtr, NumBuckets); } BucketNo += ProbeAmt++; BucketNo &= (NumBuckets-1); } }
else { auto refs_it = associations.find(disguised); if (refs_it != associations.end()) { auto &refs = refs_it->second; auto it = refs.find(key); if (it != refs.end()) { association.swap(it->second); refs.erase(it); if (refs.size() == 0) { associations.erase(refs_it);
class_rw_ext_t *extAllocIfNeeded(){ auto v = get_ro_or_rwe(); if (fastpath(v.is<class_rw_ext_t *>())) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext); } else { return extAlloc(v.get<constclass_ro_t *>(&ro_or_rw_ext)); } } class_rw_ext_t * class_rw_t::extAlloc(constclass_ro_t *ro, bool deepCopy) { runtimeLock.assertLocked();
auto rwe = objc::zalloc<class_rw_ext_t>();
rwe->version = (ro->flags & RO_META) ? 7 : 0;
method_list_t *list = ro->baseMethods(); if (list) { if (deepCopy) list = list->duplicate(); rwe->methods.attachLists(&list, 1); }
// See comments in objc_duplicateClass // property lists and protocol lists historically // have not been deep-copied // // This is probably wrong and ought to be fixed some day property_list_t *proplist = ro->baseProperties; if (proplist) { rwe->properties.attachLists(&proplist, 1); }
protocol_list_t *protolist = ro->baseProtocols; if (protolist) { rwe->protocols.attachLists(&protolist, 1); }
staticvoidmethodizeClass(Class cls, Class previously) { ///省略代码 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); ///省略代码 }
size_t count; auto processCatlist = [&](category_t * const *catlist) { for (unsigned i = 0; i < count; i++) { category_t *cat = catlist[i]; Class cls = remapClass(cat->cls); locstamped_category_t lc{cat, hi};
if (!cls) { // Category's target class is missing (probably weak-linked). // Ignore the category. if (PrintConnecting) { _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " "missing weak-linked target class", cat->name, cat); } continue; }
// Process this category. if (cls->isStubClass()) { // Stub classes are never realized. Stub classes // don't know their metaclass until they're // initialized, so we have to add categories with // class methods or properties to the stub itself. // methodizeClass() will find them and add them to // the metaclass as appropriate. if (cat->instanceMethods || cat->protocols || cat->instanceProperties || cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) { objc::unattachedCategories.addForClass(lc, cls); } } else { // First, register the category with its target class. // Then, rebuild the class's method lists (etc) if // the class is realized. if (cat->instanceMethods || cat->protocols || cat->instanceProperties) { if (cls->isRealized()) { attachCategories(cls, &lc, 1, ATTACH_EXISTING); } else { objc::unattachedCategories.addForClass(lc, cls); } }
// Realize non-lazy classes (for +load methods and static instances) for (EACH_HEADER) { classref_tconst *classlist = hi->nlclslist(&count); for (i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); if (!cls) continue; ///调试代码 确定是我们自定义的类 constchar *mangledName = cls->nonlazyMangledName(); constchar *customerClassName = "JSPerson"; if (strcmp(mangledName, customerClassName) == 0) { //打印类名 printf("%s -: non-lazy classes要研究的类: - %s\n",__func__,mangledName); }
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 } 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"); } ///调试代码 确定是我们自定义的类 constchar *mangledName = cls->nonlazyMangledName(); constchar *customerClassName = "JSPerson"; if (strcmp(mangledName, customerClassName) == 0) { //打印类名 printf("%s -: realize future classes要研究的类: - %s\n",__func__,mangledName); } realizeClassWithoutSwift(cls, nil); cls->setInstancesRequireRawIsaRecursively(false/*inherited*/); } free(resolvedFutureClasses); } ts.log("IMAGE TIMES: realize future classes");
#if SUPPORT_NONPOINTER_ISA if (isMeta) { // Metaclasses do not need any features from non pointer ISA // This allows for a faspath for classes in objc_retain/objc_release. ///元类 不是non pointer ISA cls->setInstancesRequireRawIsa(); } else { // Disable non-pointer isa for some classes and/or platforms. // Set instancesRequireRawIsa. bool instancesRequireRawIsa = cls->instancesRequireRawIsa(); bool rawIsaIsInherited = false; staticbool hackedDispatch = false;
if (DisableNonpointerIsa) { //如果我们设置变量不使用 non pointer ISA 也会是纯的isa // Non-pointer isa disabled by environment or app SDK version instancesRequireRawIsa = true; } elseif (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object")) { // hack for libdispatch et al - isa also acts as vtable pointer hackedDispatch = true; instancesRequireRawIsa = true; } elseif (supercls && supercls->getSuperclass() && supercls->instancesRequireRawIsa()) { // This is also propagated by addSubclass() // but nonpointer isa setup needs it earlier. // Special case: instancesRequireRawIsa does not propagate // from root class to root metaclass instancesRequireRawIsa = true; rawIsaIsInherited = true; }
if (instancesRequireRawIsa) { cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited); } } // SUPPORT_NONPOINTER_ISA #endif // Update superclass and metaclass in case of remapping cls->setSuperclass(supercls); cls->initClassIsa(metacls);
staticvoidmethodizeClass(Class cls, Class previously) { runtimeLock.assertLocked(); bool isMeta = cls->isMetaClass(); auto rw = cls->data(); auto ro = rw->ro(); auto rwe = rw->ext(); // Methodizing for the first time if (PrintConnecting) { _objc_inform("CLASS: methodizing class '%s' %s", cls->nameForLogging(), isMeta ? "(meta)" : ""); } ///调试代码 确定是我们自定义的类 constchar *mangledName = cls->nonlazyMangledName(); constchar *customerClassName = "JSPerson"; if (strcmp(mangledName, customerClassName) == 0) { //打印类名 if (!isMeta) { printf("%s -: non-lazy classes要研究的类: - %s\n",__func__,mangledName); } } // Install methods and properties that the class implements itself. //取出方法列表 method_list_t *list = ro->baseMethods(); if (list) { prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr); if (rwe) rwe->methods.attachLists(&list, 1); } 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);
#if DEBUG // Debug: sanity-check all SELs; log method list contents for (constauto& meth : rw->methods()) { if (PrintConnecting) { _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', cls->nameForLogging(), sel_getName(meth.name())); } ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name()); } #endif }
staticvoid prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount, bool baseMethods, bool methodsFromBundle, constchar *why) { runtimeLock.assertLocked(); if (addedCount == 0) return; // There exist RR/AWZ/Core special cases for some class's base methods. // But this code should never need to scan base methods for RR/AWZ/Core: // default RR/AWZ/Core cannot be set before setInitialized(). // Therefore we need not handle any special cases here. if (baseMethods) { ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore()); } elseif (cls->cache.isConstantOptimizedCache()) { cls->setDisallowPreoptCachesRecursively(why); } elseif (cls->allowsPreoptInlinedSels()) { #if CONFIG_USE_PREOPT_CACHES SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START]; SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END]; if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) { cls->setDisallowPreoptInlinedSelsRecursively(why); } #endif } // Add method lists to array. // Reallocate un-fixed method lists. // The new methods are PREPENDED to the method list array.
for (int i = 0; i < addedCount; i++) { method_list_t *mlist = addedLists[i]; ASSERT(mlist); // Fixup selectors if necessary if (!mlist->isFixedUp()) { //核心代码 fixupMethodList(mlist, methodsFromBundle, true/*sort*/); } }
// If the class is initialized, then scan for method implementations // tracked by the class's flags. If it's not initialized yet, // then objc_class::setInitialized() will take care of it. if (cls->isInitialized()) { objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount); objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount); objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount); } }
staticvoid fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort) { runtimeLock.assertLocked(); ASSERT(!mlist->isFixedUp()); // 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.setName(sel_registerNameNoLock(name, bundleCopy)); } } // Sort by selector address. // Don't try to sort small lists, as they're immutable. // Don't try to sort big lists of nonstandard size, as stable_sort // won't copy the entries properly. if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) { method_t::SortBySELAddress sorter; std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter); } // Mark method list as uniqued and sorted. // Can't mark small lists, since they're immutable. if (!mlist->isSmallList()) { mlist->setFixedUp(); } }
voidenviron_init(void) { ///省略代码 // Print OBJC_HELP and OBJC_PRINT_OPTIONS output. if (PrintHelp || PrintOptions) { if (PrintHelp) { _objc_inform("Objective-C runtime debugging. Set variable=YES to enable."); _objc_inform("OBJC_HELP: describe available environment variables"); if (PrintOptions) { _objc_inform("OBJC_HELP is set"); } _objc_inform("OBJC_PRINT_OPTIONS: list which options are set"); } if (PrintOptions) { _objc_inform("OBJC_PRINT_OPTIONS is set"); } ///这里如果满足条件 会打印所有的环境变量 for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) { constoption_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); } } }
可以看出来就是一些环境变量的初始化,参看这段代码,我们可以打印环境变量。
将条件去掉直接调用for循环
1 2 3 4 5
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) { constoption_t *opt = &Settings[i]; _objc_inform("%s: %s", opt->env, opt->help); _objc_inform("%s is set", opt->env); }
通过终端命令export OBJC_hrlp = 1,打印环境变量
我们平时项目中可能会有几个环境变量可以在我们的xcode配置(target -- Edit Scheme -- Run --Arguments -- Environment Variables)一下对应的值,达到修改环境变量的目的:
staticvoid _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)(); } } }
void _imp_implementationWithBlock_init(void) { #if TARGET_OS_OSX // Eagerly load libobjc-trampolines.dylib in certain processes. Some // programs (most notably QtWebEngineProcess used by older versions of // embedded Chromium) enable a highly restrictive sandbox profile which // blocks access to that dylib. If anything calls // imp_implementationWithBlock (as AppKit has started doing) then we'll // crash trying to load it. Loading it here sets it up before the sandbox // profile is enabled and blocks it. // // This fixes EA Origin (rdar://problem/50813789) // and Steam (rdar://problem/55286131) if (__progname && (strcmp(__progname, "QtWebEngineProcess") == 0 || strcmp(__progname, "Steam Helper") == 0)) { Trampolines.Initialize(); } #endif }
// Perform first-time initialization if necessary. // This function is called before ordinary library initializers. // fixme defer initialization until an objc-using image is found? if (firstTime) { preopt_init(); }
if (PrintImages) { _objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount); }
// Find all images with Objective-C metadata. hCount = 0;
// Count classes. Size various table based on the total. int totalClasses = 0; int unoptimizedTotalClasses = 0; { uint32_t i = mhCount; while (i--) { const headerType *mhdr = (const headerType *)mhdrs[i];
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses); if (!hi) { // no objc data in this entry continue; } if (mhdr->filetype == MH_EXECUTE) { // Size some data structures based on main executable's size #if __OBJC2__ // If dyld3 optimized the main executable, then there shouldn't // be any selrefs needed in the dynamic map so we can just init // to a 0 sized map if ( !hi->hasPreoptimizedSelectors() ) { size_t count; _getObjc2SelectorRefs(hi, &count); selrefCount += count; _getObjc2MessageRefs(hi, &count); selrefCount += count; } #else _getObjcSelectorRefs(hi, &selrefCount); #endif #if SUPPORT_GC_COMPAT // Halt if this is a GC app. if (shouldRejectGCApp(hi)) { _objc_fatal_with_reason (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, OS_REASON_FLAG_CONSISTENT_FAILURE, "Objective-C garbage collection " "is no longer supported."); } #endif } hList[hCount++] = hi; if (PrintImages) { _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", hi->fname(), mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", hi->info()->isReplacement() ? " (replacement)" : "", hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "", hi->info()->optimizedByDyld()?" (preoptimized)":""); } } }
// Perform one-time runtime initialization that must be deferred until // the executable itself is found. This needs to be done before // further initialization. // (The executable may not be present in this infoList if the // executable does not contain Objective-C code but Objective-C // is dynamically loaded later. if (firstTime) { sel_init(selrefCount); arr_init();
#if SUPPORT_GC_COMPAT // Reject any GC images linked to the main executable. // We already rejected the app itself above. // Images loaded after launch will be rejected by dyld.
for (uint32_t i = 0; i < hCount; i++) { auto hi = hList[i]; auto mh = hi->mhdr(); if (mh->filetype != MH_EXECUTE && shouldRejectGCImage(mh)) { _objc_fatal_with_reason (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, OS_REASON_FLAG_CONSISTENT_FAILURE, "%s requires Objective-C garbage collection " "which is no longer supported.", hi->fname()); } } #endif
#if TARGET_OS_OSX // Disable +initialize fork safety if the app is too old (< 10.13). // Disable +initialize fork safety if the app has a // __DATA,__objc_fork_ok section.
// if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) { // DisableInitializeForkSafety = true; // if (PrintInitializing) { // _objc_inform("INITIALIZE: disabling +initialize fork " // "safety enforcement because the app is " // "too old.)"); // } // }
for (uint32_t i = 0; i < hCount; i++) { auto hi = hList[i]; auto mh = hi->mhdr(); if (mh->filetype != MH_EXECUTE) continue; unsignedlongsize; if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) { DisableInitializeForkSafety = true; if (PrintInitializing) { _objc_inform("INITIALIZE: disabling +initialize fork " "safety enforcement because the app has " "a __DATA,__objc_fork_ok section"); } } break; // assume only one MH_EXECUTE image } #endif } if (hCount > 0) { ///核心方法 _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); }
firstTime = NO; // Call image load funcs after everything is set up. for (auto func : loadImageFuncs) { for (uint32_t i = 0; i < mhCount; i++) { func(mhdrs[i]); } } }
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_program_sdk_at_least(dyld_platform_version_macOS_10_11)) { // DisableNonpointerIsa = true; // if (PrintRawIsa) { // _objc_inform("RAW ISA: disabling non-pointer isa because " // "the app is too old."); // } // }
// 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; unsignedlongsize; 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(); } ///初始化TaggedPointer混淆 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 // objc::unattachedCategories.init(32); // objc::allocatedClasses.init(); //负载因子 int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3; //创建一张类的总表,包含所有的类 gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize); ts.log("IMAGE TIMES: first time tasks"); }
// 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; } //从mach-o读取类 classref_tconst *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) { //cls 目前没有名字 Class cls = (Class)classlist[i]; //关联类cls的名字 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");
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) { constchar *mangledName = cls->nonlazyMangledName(); ///以下代码 为笔者添加 方便调试到自定义的类(因为所以类都会执行这里,我们只关心我们定义的类) constchar *customerClassName = "JSPerson"; if (strcmp(mangledName, LGPersonName) == 0) { //打印类名 printf("%s -: 要研究的类: - %s\n",__func__,mangledName); } if (missingWeakSuperclass(cls)) { // No superclass (probably weak-linked). // Disavow any knowledge of this subclass. if (PrintConnecting) { _objc_inform("CLASS: IGNORING class '%s' with " "missing weak-linked superclass", cls->nameForLogging()); } addRemappedClass(cls, nil); cls->setSuperclass(nil); return nil; } cls->fixupBackwardDeployingStableSwift();
Class replacing = nil; if (mangledName != nullptr) { 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()); }
// Manually set address-discriminated ptrauthed fields // so that newCls gets the correct signatures. newCls->setSuperclass(cls->getSuperclass()); newCls->initIsa(cls->getIsa());
replacing = cls; cls = newCls; } } if (headerIsPreoptimized && !replacing) { // class list built in shared cache // fixme strict assert doesn't work because of duplicates // ASSERT(cls == getClass(name)); ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName)); } else { if (mangledName) { //some Swift generic classes can lazily generate their names ///给类添加名字 addNamedClass(cls, mangledName, replacing); } else { ///元类也处理 Class meta = cls->ISA(); constclass_ro_t *metaRO = meta->bits.safe_ro(); ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass."); ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class."); } ///添加到class表 addClassTableEntry(cls); } // for future reference: shared cache never contains MH_BUNDLEs if (headerIsBundle) { cls->data()->flags |= RO_FROM_BUNDLE; cls->ISA()->data()->flags |= RO_FROM_BUNDLE; } return cls; }
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); }