我们知道,OC 是 C 语言的超集,是对 C 和 C++ 的进一步封装,一开始学习 OC 这门语言的时候,我们就被灌输过一句话:对象存储在堆内存,变量存储在栈内存,而 runtime 告诉我们类是对 C 和 C++ 中结构体的封装,而结构体是值类型(值类型 vs 引用类型),肯定是存储在栈上的,这不是自相矛盾吗?另外,OC1.0 是完全对 C 语言的封装,C 语言的结构体是不能声明和实现函数的,到底是怎么回事呢?现在我们用结构体实现一个简单的类:
这段代码和上述代码的差异为,在 if 条件语句中调用函数的方式变成了函数指针而不是简单的函数调用。它的动态性体现在,编译器在编译期仅仅获取函数的首地址,将指向函数的首地址硬编码进汇编指令集,而不是将整个函数的指令全部硬编码,到运行时再去决定调用那个函数(访问哪个函数的内存)。如果你在运行时强制将这个本来指向某个函数的指针指向另一个函数,那么这就是所谓的方法交换。
/// A pointer to the function of a method implementation. #if !OBJC_OLD_DISPATCH_PROTOTYPES typedefvoid(*IMP)(void/* id, SEL, ... */ ); #else typedefid(*IMP)(id, SEL, ...); #endif
/*********************************************************************** * lookUpImpOrForward. * The standard IMP lookup. * initialize==NO tries to avoid +initialize (but sometimes fails) * cache==NO skips optimistic unlocked lookup (but uses cache elsewhere) * Most callers should use initialize==YES and cache==YES. * inst is an instance of cls or a subclass thereof, or nil if none is known. * If cls is an un-initialized metaclass then a non-nil inst is faster. * May return _objc_msgForward_impcache. IMPs destined for external use * must be converted to _objc_msgForward or _objc_msgForward_stret. * If you don't want forwarding at all, use lookUpImpOrNil() instead. **********************************************************************/ IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) { Class curClass; IMP imp = nil; Method meth; bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup if (cache) { imp = cache_getImp(cls, sel); if (imp) return imp; }
if (!cls->isRealized()) { rwlock_writer_tlock(runtimeLock); realizeClass(cls); }
if (initialize && !cls->isInitialized()) { _class_initialize (_class_getNonMetaClass(cls, inst)); // If sel == initialize, _class_initialize will send +initialize and // then the messenger will send +initialize again after this // procedure finishes. Of course, if this is not being called // from the messenger then it won't happen. 2778172 }
// The lock is held to make method-lookup + cache-fill atomic // with respect to method addition. Otherwise, a category could // be added but ignored indefinitely because the cache was re-filled // with the old value after the cache flush on behalf of the category. retry: runtimeLock.read();
curClass = cls; while ((curClass = curClass->superclass)) { // Superclass cache. imp = cache_getImp(curClass, sel); if (imp) { if (imp != (IMP)_objc_msgForward_impcache) { // Found the method in a superclass. Cache it in this class. log_and_fill_cache(cls, imp, sel, inst, curClass); goto done; } else { // Found a forward:: entry in a superclass. // Stop searching, but don't cache yet; call method // resolver for this class first. break; } }
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) { runtimeLock.unlockRead(); _class_resolveMethod(cls, sel, inst); // Don't cache the result; we don't hold the lock so it may have // changed already. Re-do the search from scratch instead. triedResolver = YES; goto retry; }
// No implementation found, and method resolver didn't help. // Use forwarding.