动态class_Objective-C运行时-动态特性

本文深入探讨Objective-C的动态特性,包括动态类型、动态绑定、动态方法决议和动态加载。动态类型允许运行时确定对象类型,动态绑定通过选择器实现方法的动态查找。动态方法决议在找不到实现时进行,而动态加载涉及类和类别的运行时加载。文章还详细解析了isa_t联合、内存管理和autoreleasepool等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Objective-C的动态性体现在四个方面:动态类型(Dynamic typing)、动态绑定(Dynamic binding 找到真的的实现)、动态方法决议(Dynamic Method Resolution 添加新的实现)、动态加载(Dynamic loading)。

动态类型(Dynamic typing)

Dynamic typing,使用任意类型id,在运行时确定对象类型(在编译时确定对象的类型是静态类型),由对象类型决定指针的类型,也可以通过isKindofClass方法,动态判断对象的最后的类型。 在Dynamic typing作用下,实例对象的类别可延迟到运行时确定,是数据动态性。

动态绑定(Dynamic binding 找到真的的实现)

Objective-C是面向对象的编程语言,方法是第二个维度,Dynamic binding便是方法在运行时确定的机制,即消息传递机制。为此Objective-C构造了选择器,把C中的单维度的函数指针二维化,运行时动态的查找选择器对应的函数指针,可以灵活的构造选择器到多个函数指针的映射。

void objc_msgSend (id self, SEL cmd, ...)

⚠️动态绑定使用了动态类型,但动态类型不是它的基础,离开它一样可以实现动态绑定,但没有丰富的现实意义。

如果动态绑定失败(没有找到实现的方法),走动态方法决议流程。

动态方法决议(Dynamic Method Resolution )

_objc_msgForward在进行消息转发的过程中会涉及以下这几个方法:
0、查找类体系中的实现
1、resolveInstanceMethod:方法 (或 resolveClassMethod:)。添加新的实现
2、forwardingTargetForSelector:方法 快速转发-备用接受者
3、methodSignatureForSelector:方法 完整转发-生成方法签名
4、forwardInvocation:方法 完整转发-调用
5、doesNotRecognizeSelector: 方法 异常

2c9f4f4296e7022fdc2cbcaa9645ad0f.png

0958372a47eec9724cd0ecd982f6ea50.png
void dynamicMethodIMP(id self, SEL _cmd) {
    // implementation ....
}
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically)) {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}

Dynamic loading

类和类别运行时动态加载,这里暂不讨论。

Dynamic typing

Objective-C 是面向对象的编程语言,具有封装、继承和多态三大特性,同时具有category这一伟大的特性,与继承相媲美。Dynamic typing不仅仅提供运行时确定对象类型,也提供了运行时修改类型的封装的能力,这才是Objective-C所独具的特色与核心。

struct objc_object {
private:
    isa_t isa;
public:
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();//非标号指针
    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    ....
}

/* Use `Class` instead of `struct objc_class *` */
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
    class_rw_t *data() {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......
}


typedef struct objc_class *Class;
typedef struct objc_object *id;

Class是定义为objc_class结构体的指针,id定义为objc_object结构体的指针,objc_class继承objc_object,在Objective-C中万物皆对象。Class承载了对象的数据结构信息与操作,注意到从class_data_bits_t到class_rw_t的转换,class_rw_t便是那个承载了诸多动态性的结构。

struct class_rw_t {// 8 bytes
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;//
    
    method_array_t methods;     //动态添加
    property_array_t properties;//动态添加
    protocol_array_t protocols;//动态添加
    
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
    
#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif
    
    void setFlags(uint32_t set){
        OSAtomicOr32Barrier(set, &flags);
    }
    void clearFlags(uint32_t clear){
        OSAtomicXor32Barrier(clear, &flags);
    }
    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear){
        assert((set & clear) == 0);
        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf,
                                                newf,
                                                (volatile int32_t *)&flags));
    }
};

class_rw_t结构提供了可扩展的方法列表、属性列表和协议列表,可通过类别与协议向类中添加方法与属性,不必使用继承。class_rw_t结构提供了类结构变化控制的接口,而class_ro_t封装了继承体系中不变的成分,如实例变量,和基类中的方法和协议(在加载时会放到RW对应结构的对应列表中)。class_ro_t一但编译完成,便不可继续添加实例变量,如通过类别或是协议添加属性会引起崩溃,可通过关联存储实现(内部通二级哈希表实现:以指针非操作结果(隐藏指针机制)为key,在全局哈希表中找到对象对应的哈希表,通过对象属性定位属性)。

struct class_ro_t {
    uint32_t flags;  ////////////////////////
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved; //not set directly
#endif
    
    const uint8_t * ivarLayout;
    const char * name;
    method_list_t * baseMethodList; //not set directly
    protocol_list_t * baseProtocols; //not set directly
    
    const ivar_list_t * ivars;//not set directly, create by compiler, read from image
    const uint8_t * weakIvarLayout;//point to the memory that stores the value of ivar wjf
    
    property_list_t *baseProperties; //not set directly
    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

Objective-C指向类指针,即Class,占用5个bytes,而内部最低对其是4bytes,所以isa占用8bytes,其后是实例变量空间,在类等级中同样遵守4bytes对其原则。如Test实例占用8+4=12bytes空间,Test1实例占用8+4+4=16bytes空间,Test2实例占用8+4=12bytes空间(flag、flag2、flag3、flag4占据连续的四个byte),Test3占用8+4=12bytes空间(在flag与flag2之间有一个空闲byte)。

@interface Test : NSObject
@property BOOL flag;
@end

@interface Test1 : Test
@property BOOL flag1; 
@end

@interface Test2 : NSObject
@property BOOL flag;  
@property BOOL flag2; 
@property BOOL flag3; 
@property BOOL flag4;  
@end

@interface Test3 : NSObject
@property BOOL flag;
@property short flag2;
@end
  Test * test = [Test new];
  test.flag = 1;
  test.flag3 = 4; 

f3ad39490bb72bba5c6f061303abf84d.png
Test3内存布局
  • 父类与子类不共用4bytes对其规则
  • 父类的实例变量更靠近isa
  • 对齐规则为4bytes规则+类型对其规则

isa_t联合

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
    struct {//__arm64__
        uintptr_t nonpointer        : 1; ////true tagged指针,值直接存储在 地址中
        uintptr_t has_assoc         : 1; //是否关联存储

        uintptr_t has_cxx_dtor      : 1; //是否有析构器
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 间接地址
        uintptr_t magic             : 6;

        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;//45
    #       define RC_ONE   (1ULL<<45)
    #       define RC_HALF  (1ULL<<18)
    };
}

在__arm64__与__x86_64__两个架构上struct具体的字段相同,但是分布不同。 __ARM_ARCH_7K__架构上的struct具体的字段有所不同。

__ARM_ARCH_7K__具有所谓的索引类别指针,即indexcls,类对象存储在一个数组中,indexcls的值为类对象在数组中的下标。而__arm64__与__x86_64__直接存储类对象的地址。

struct {//__ARM_ARCH_7K__
        uintptr_t nonpointer        : 1;    //true tagged指针,值直接存储在 地址中
        uintptr_t has_assoc         : 1;

        uintptr_t indexcls          : 15;   //类对象索引地址,类对象存储在一个数组中,通过索引查找
        uintptr_t magic             : 4;
        uintptr_t has_cxx_dtor      : 1;//up

        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 7;//25
    #       define RC_ONE   (1ULL<<25)
    #       define RC_HALF  (1ULL<<6)
    };
  • nonpointer:该实例对象的指针是否是标号指针
  • has_assoc:该实例对象是否有关联存储的对象,true在析构时需要移除关联的对象
  • has_cxx_dtor:是否有C++析构函数
  • weakly_referenced:实例对象是否被其它对象若引用,如果为true析构时需要把若表中指针置为nil
  • has_sidetable_rc:是否在边表中存储引用计数值
void *objc_destructInstance(id obj) 
{//移除关联的对象
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }

    return obj;
}
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        weak_clear_no_lock(&table.weak_table, (id)this);//边表移除若引用
    }
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

标号指针

inline Class objc_object::ISA() {
    assert(!isTaggedPointer());         //标号指针
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {        //不是指针,是下标值
        uintptr_t slot = isa.indexcls;// index
        return classForIndex((unsigned)slot);//looking table
    }
    return (Class)isa.bits;        //类对象的指针,错误情况
#else
    return (Class)(isa.bits & ISA_MASK);//bit operation
#endif
}

目前常见的支持标号指针的有5个类:NSString、NSNumber、NSIndexPath、NSManagedObjectID、NSDate。

内存管理-边表

objective-c runtime对内存的管理是基于几个十分重要的数据结构与概念展开的。

  • 带化映射模版类StripedMap,与此对应的概念是带化锁
  • 指针伪装模版类DisguisedPtr<T>,与此对应的概念是指针伪装
  • 数据结构弱表weak_table_t弱实体weak_entry_t,与它们对应的概念是自动释放池
  • 结构体边表SideTable,与其对应的概念是引用计数映射

SideTableBuf

  • SideTableBuf { StripedMap { SideTable *8 {
  • spinlock_t slock;
  • RefcountMap refcnts; //简单的二次探测哈希表,擅长支持小的键和值, id -> rcount
  • weak_table_t weak_table;
  • } } }
  • weak_table_t { weak_entry_t* //数组,使用循环查找 }
  • //通过不精确定位+循环查找特定对象对应的weak_entry_t

SideTableBuf是一个静态内存区域,new StripedMap<SideTable>在其上创建了对象,巧妙的避开了初始化顺序问题。代码如下:

alignas(StripedMap<SideTable>) static uint8_t
  SideTableBuf[sizeof(StripedMap<SideTable>)];//placement new
  static void SideTableInit() {
  new (SideTableBuf) StripedMap<SideTable>();
  }
  static StripedMap<SideTable>& SideTables() {
  return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
  }

SideTableBuf是一个外部不可见的静态内存区块,存储StripedMap<SideTable>对象。它是内存管理的基础,其它的功能与特性都是基于这个插槽而展开的。为了理解内存管理机制的运作,我们首先药介绍StripedMap<T>和SideTable。

带化映射模版类StripedMap

StripedMap<T> 实现void* 到T的映射,objective-c中为id到SideTable的映射(含义是这个对象的生命周期由哪个SideTable管理),即各种结构体指针(id)到SideTable的映射。 在嵌入式架构中,StripedMap<T>一个典型的应用是8个内旋锁的映射查找,以提高并发访问的速度。有如下特化:

StripedMap<spinlock_t>{
 enum { CacheLineSize = 64 };
 enum { StripeCount = 8 };
 struct PaddedT {
 T value alignas(CacheLineSize);
 };
 PaddedT array[StripeCount];
 static unsigned int indexForPointer(const void *p) {
 uintptr_t addr = reinterpret_cast<uintptr_t>(p);
 return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
 }
public:
  T& operator[] (const void *p) {                            //cast void *  to  T&
  return array[indexForPointer(p)].value; 
  }
  const T& operator[] (const void *p) const {        //cast void *  to  const T&
  return const_cast<StripedMap<T>>(this)[p]; 
  }
    ..........
}

array是一个长度为8的数组,T类型强制为64位对齐。在运行时中已知应用实例:StripedMap<spinlock_t>、StripedMap<SyncList>和StripedMap<SideTable> 。。

StripedMap中没有用到锁,对内存块SideTableBuf中对象是只读的,对StripedMap封装的8个全局表的操作在多线程环境下仍是线程安全的

SideTable

辅助表,或是边表,它是objective-c内存管理的核心类。所有对象共享8个运行时维护的全局边表,特定的对象只对应于单个边表。运行时通过边表维护的唯一的一个自旋锁,保证多线程环境下对象操作的有效性。

// Template parameters.
 enum HaveOld { DontHaveOld = false, DoHaveOld = true };
 enum HaveNew { DontHaveNew = false, DoHaveNew = true };
 struct SideTable {
   spinlock_t slock;
   RefcountMap refcnts;///
   weak_table_t weak_table;
   SideTable() {
   memset(&weak_table, 0, sizeof(weak_table));
 }
 ~SideTable() {
   _objc_fatal("Do not delete SideTable.");
 }
 void lock() { slock.lock(); }

 void unlock() { slock.unlock(); }
 void forceReset() { slock.forceReset(); }
 // Address-ordered lock discipline for a pair of side tables.
 template<HaveOld, HaveNew>
  static void lockTwo(SideTable *lock1, SideTable *lock2);
 template<HaveOld, HaveNew>
  static void unlockTwo(SideTable *lock1, SideTable *lock2);
 };

DenseMap是llvm库中的类,是一个简单的二次探测哈希表,擅长支持小的键和值。DisguisedPtr<T>通过运算使指针隐藏于系统工具(如LEAK工具),同时保持指针的能力,其作用是通过计算把保存的T的指针隐藏起来,实现指针到整数的全射。其中RefcountMap定义为:

 typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;

typedef objc::DenseMap<对象指针运算得到的整数,对象的引用计算值,true> RefcountMap,边表就是通过RefcountMap保存隐藏的且可恢复的对象指针与引用计数值,当引用计数变为0时,通过weak_table_t置空所有的weak引用,这就是weak置空的大体触发机制。

template <typename T>
class DisguisedPtr {
 uintptr_t value;    // unsigned long
 static uintptr_t disguise(T* ptr) {         //指针隐藏
 return -(uintptr_t)ptr;
 }
 static T* undisguise(uintptr_t val) {  //指针显露
 return (T*)-val;
 }
    .......
};

DenseMap存储了对象指针与引用计数的键值对,运行时一共维护8个SideTable,对应一共有8个DenseMap。SideTable通过一个自旋锁控制对DenseMap集合的访问。所以对象如何在这8个SideTable之间(DenseMap之间)分布存储是提高系统并发效率的关键。

weak_table_t

运行时一共维护8个SideTable,对应一共有8个DenseMap,同样也是8个weak_table_t表。

#define WEAK_INLINE_COUNT 4
#define REFERRERS_OUT_OF_LINE 2

struct weak_entry_t {
  DisguisedPtr<objc_object> referent;
  union {
  struct {
  weak_referrer_t *referrers;//若引用者的列表
  uintptr_t  out_of_line_ness : 2;
  uintptr_t  num_refs : PTR_MINUS_2;//62 or 30
  uintptr_t  mask;
  uintptr_t  max_hash_displacement;
  };
struct {
 weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];//4

  };
  };
  bool out_of_line() {
  return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
  }
 weak_entry_t& operator=(const weak_entry_t& other) {
  memcpy(this, &other, sizeof(other));
  return *this;
  }
  weak_entry_t(objc_object *newReferent,
  objc_object **newReferrer)
  : referent(newReferent){
  inline_referrers[0] = newReferrer;
  for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
  inline_referrers[i] = nil;
  }
  }
};

struct weak_table_t {
  weak_entry_t *weak_entries;
  size_t  num_entries;
  uintptr_t mask;
  uintptr_t max_hash_displacement;
};
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
  assert(referent);
  weak_entry_t *weak_entries = weak_table->weak_entries;
  if (!weak_entries) return nil;
  size_t begin = hash_pointer(referent) & weak_table->mask;
  size_t index = begin;
  size_t hash_displacement = 0;

  while (weak_table->weak_entries[index].referent != referent) {
  index = (index+1) & weak_table->mask;
 
   if (index == begin) bad_weak_table(weak_table->weak_entries);
  hash_displacement++;
  if (hash_displacement > weak_table->max_hash_displacement) {
    return nil;
  }
  }
 return &weak_table->weak_entries[index];
}

weak_table_t维护一个weak_entry_t *,指向一个weak_entry_t一维数组,通过不精确定位+循环查找特定对象对应的weak_entry_t。

autoreleasepool

AutoreleasePool底层实现原理​www.jianshu.com
0ce13abe48372b3f86da4f54196f25a8.png

使用双向链表承载延迟释放信息,使用池边界(POOL_BOUNDARY)分割不同的嵌套池。

Dynamic binding 动态绑定

动态绑定,即从编码时的选择器,到运行时通过方法查找,找到对应的函数指针的过程。

方法缓存前查找过程

在类的特定方法列表中查找方法

struct old_method {
    SEL method_name;//sel 是方法名字
    char *method_types;
    IMP method_imp;
};
static inline old_method *_findMethodInList(old_method_list * mlist, SEL sel) {
    int i;
    if (!mlist) return nil;
    for (i = 0; i < mlist->method_count; i++) {
        old_method *m = &mlist->method_list[i];
        if (m->method_name == sel) {//sel 是方法名字
            return m;
        }
    }
    return nil;
}

类可能有多个,一个或是没有方法列表

static inline old_method * _findMethodInClass(Class cls, SEL sel) {
    // Flattened version of nextMethodList(). The optimizer doesn't 
    // do a good job with hoisting the conditionals out of the loop.
    // Conceptually, this looks like:
    // while ((mlist = nextMethodList(cls, &iterator))) {
    //     old_method *m = _findMethodInList(mlist, sel);
    //     if (m) return m;
    // }

    if (!cls->methodLists) {
        // No method lists.
        return nil;
    }
    else if (cls->info & CLS_NO_METHOD_ARRAY) {
        // One method list.  单个方法列表
        old_method_list **mlistp;
        mlistp = (old_method_list **)&cls->methodLists;
        *mlistp = fixupSelectorsInMethodList(cls, *mlistp);
        return _findMethodInList(*mlistp, sel);
    }
    else {
        // Multiple method lists. 多个方法列表
        old_method_list **mlistp;
        for (mlistp = cls->methodLists; 
             *mlistp != nil  &&  *mlistp != END_OF_METHODS_LIST; 
             mlistp++) 
        {
            old_method *m;
            *mlistp = fixupSelectorsInMethodList(cls, *mlistp);
            m = _findMethodInList(*mlistp, sel);
            if (m) return m;
        }
        return nil;
    }
}
static Method _class_getMethodNoSuper_nolock(Class cls, SEL sel)
{//对外接口
    methodListLock.assertLocked();
    return (Method)_findMethodInClass(cls, sel);
}

查找到方法后,获取实现指针

// called by a debugging check in _objc_insertMethods
IMP findIMPInClass(Class cls, SEL sel)
{
    old_method *m = _findMethodInClass(cls, sel);
    if (m) return m->method_imp;
    else return nil;
}

方法查找有两个过程,一个是不查找父类的方法列表lookupMethodInClassAndLoadCache,在析构中使用object_cxxDestructFromClass。

一个是查找父类的方法列表lookUpImpOrForward,在消息发送时使用。它是标准的IMP查找函数。其步骤如下:

  1. 查找类自己的缓存,找到返回实现,否则:
  2. 查找自己的方法列表(分类与协议中的方法,在加载的时候已经加入到类的方法列表中),找到加入类的缓存列表,否则:
  3. 查找父类,同样1 2 的过程,只不过这次操作是父类的缓存列表。
  4. 如果在整个类链上都没有找到,则进入方法决议_class_resolveMethod
  5. 如果方法决议也没有得到有效的实现,进入方法转发_objc_msgForward_impcache
/***********************************************************************
* 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 methodPC = nil;
    Method meth;
    bool triedResolver = NO;

    methodListLock.assertUnlocked();

    // Optimistic cache lookup
    if (cache) {
        methodPC = _cache_getImp(cls, sel);
        if (methodPC) return methodPC;    
    }

    // Check for freed class
    if (cls == _class_getFreedObjectClass())
        return (IMP) _freedHandler;

    // Check for +initialize
    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:
    methodListLock.lock();

    // Try this class's cache.

    methodPC = _cache_getImp(cls, sel);
    if (methodPC) goto done;

    // Try this class's method lists.
    meth = _class_getMethodNoSuper_nolock(cls, sel);
    if (meth) {
        log_and_fill_cache(cls, cls, meth, sel);
        methodPC = method_getImplementation(meth);
        goto done;
    }

    // Try superclass caches and method lists.
    curClass = cls;
    while ((curClass = curClass->superclass)) {
        // Superclass cache.
        meth = _cache_getMethod(curClass, sel, _objc_msgForward_impcache);
        if (meth) {
            if (meth != (Method)1) {
                // Found the method in a superclass. Cache it in this class.
                log_and_fill_cache(cls, curClass, meth, sel);
                methodPC = method_getImplementation(meth);
                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;
            }
        }

        // Superclass method list.
        meth = _class_getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
            log_and_fill_cache(cls, curClass, meth, sel);
            methodPC = method_getImplementation(meth);
            goto done;
        }
    }

    // No implementation found. Try method resolver once.
    if (resolver  &&  !triedResolver) {
        methodListLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.
    _cache_addForwardEntry(cls, sel);
    methodPC = _objc_msgForward_impcache;

 done:
    methodListLock.unlock();

    return methodPC;
}

https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/DynamicTyping.html

https://www.tutorialspoint.com/objective_c/objective_c_dynamic_binding.htm

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html#//apple_ref/doc/uid/TP40008048-CH102-SW1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值