深入淺出 Runtime(二):數(shù)據(jù)結(jié)構(gòu)

Runtime 系列文章

深入淺出 Runtime(一):初識(shí)
深入淺出 Runtime(二):數(shù)據(jù)結(jié)構(gòu)
深入淺出 Runtime(三):消息機(jī)制
深入淺出 Runtime(四):super 的本質(zhì)
深入淺出 Runtime(五):具體應(yīng)用
深入淺出 Runtime(六):相關(guān)面試題

網(wǎng)絡(luò)配圖

目錄

  • 1. objc_object
  • 2. objc_class
    2.1 class_data_bits_t
    2.2 cache_t
    ?2.2.1 緩存查找流程
    ?2.2.2 緩存添加流程
    ?2.2.3 緩存擴(kuò)容流程
  • 3. isa指針
    3.1 isa 與 superclass 指針指向
    3.2 類對(duì)象(class)與元類對(duì)象(meta-class)
    3.3 獲得 class 或者 meta-class 的方式
  • 4. method_t
    4.1 SEL
    4.2 IMP
    4.3 Type Encodings

1. objc_object

Objective-C的面向?qū)ο蠖际腔?code>C/C++的數(shù)據(jù)結(jié)構(gòu)——結(jié)構(gòu)體實(shí)現(xiàn)的饼暑。
我們平時(shí)使用的所有對(duì)象都是id類型,id類型對(duì)象對(duì)應(yīng)到runtime中楷掉,就是objc_object結(jié)構(gòu)體绍些。

// A pointer to an instance of a class.
typedef struct objc_object *id;
struct objc_object {
private:
    isa_t isa;
    /*...
      isa操作相關(guān)
      弱引用相關(guān)
      關(guān)聯(lián)對(duì)象相關(guān)
      內(nèi)存管理相關(guān)
      ...
     */
};

2. objc_class

Class指針用來(lái)指向一個(gè) Objective-C 的類炫加,它是objc_class結(jié)構(gòu)體類型汛聚,所以class嵌赠、meta-class底層結(jié)構(gòu)都是objc_class結(jié)構(gòu)體叫榕,objc_class繼承自objc_object,所以它也有isa指針详炬,它也是對(duì)象盐类。

// An opaque type that represents an Objective-C class.
typedef struct objc_class *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();
    }
};

2.1 class_data_bits_t

  • class_data_bits_t主要是對(duì)class_rw_t的封裝,可以通過(guò)bits & FAST_DATA_MASK獲得class_rw_t呛谜。
struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits;
public:
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};
  • class_rw_t代表了類相關(guān)的讀寫信息在跳,它是對(duì)class_ro_t的封裝;
  • class_rw_t中主要存儲(chǔ)著類的方法列表隐岛、屬性列表猫妙、協(xié)議列表等;
  • class_rw_t里面的methods聚凹、properties割坠、protocols都繼承于list_array_tt二維數(shù)組,是可讀可寫的妒牙,包含了類的初始內(nèi)容彼哼、分類的內(nèi)容。
struct class_rw_t {
    // 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;   // 協(xié)議列表

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
};
  • class_ro_t代表了類相關(guān)的只讀信息湘今;
  • class_ro_t中主要存儲(chǔ)著類的成員變量列表敢朱、類名等;
  • class_ro_t里面的baseMethodList摩瞎、baseProtocols拴签、ivarsbaseProperties是一維數(shù)組愉豺,是只讀的篓吁,包含了類的初始內(nèi)容;
  • 一開(kāi)始類的信息都存放在class_ro_t里蚪拦,當(dāng)程序運(yùn)行時(shí)杖剪,經(jīng)過(guò)一系列的函數(shù)調(diào)用棧,在realizeClass()函數(shù)中驰贷,將class_ro_t里的東西和分類的東西合并起來(lái)放到class_rw_t里盛嘿,并讓bits指向class_rw_t
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // instance對(duì)象占用的內(nèi)存空間
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;    
    const char * name;  // 類名
    method_list_t * baseMethodList;  
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // 成員變量列表
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};
  • method_array_tmethod_list_t括袒。
method_array_t 與 method_list_t

2.2 cache_t

  • 用于快速查找方法執(zhí)行函數(shù)次兆;
  • 是可增量擴(kuò)展的哈希表結(jié)構(gòu),用哈希表來(lái)緩存曾經(jīng)使用過(guò)的方法锹锰,可以提高方法的查找速度(空間換時(shí)間:犧牲內(nèi)存空間來(lái)?yè)Q取執(zhí)行效率)芥炭;
  • 是局部性原理的最佳應(yīng)用(比如一些方法調(diào)用的頻率高漓库,存放到cache中,下一次調(diào)用這些方法的命中率就會(huì)更高些)园蝠;
  • hash 函數(shù)式為 f(@selector()) = index, @selector() & _mask渺蒿;
  • 當(dāng)我們調(diào)用過(guò)一個(gè)方法后,runtime會(huì)將這個(gè)方法緩存到cache中彪薛,下次再調(diào)用此方法的時(shí)候茂装,runtime會(huì)優(yōu)先去cache中查找。
struct cache_t {
    struct bucket_t *_buckets;  // 哈希表
    mask_t _mask;               // 哈希表的長(zhǎng)度 - 1
    mask_t _occupied;           // 已經(jīng)緩存的方法數(shù)量
};

struct bucket_t {
private:
    cache_key_t _key;  // SEL
    IMP _imp;          // IMP 函數(shù)的內(nèi)存地址
};

2.2.1 緩存查找流程

//objc-cache.mm(objc4)
bucket_t * cache_t::find(cache_key_t k, id receiver)  // 根據(jù) k 即 @selector 進(jìn)行查找
{
    assert(k != 0);

    bucket_t *b = buckets();          // 獲取_buckets
    mask_t m = mask();                // 獲取_mask
    mask_t begin = cache_hash(k, m);  // 計(jì)算起始索引
    mask_t i = begin;
    do {
        // 根據(jù)索引 i 從 _buckets 哈希表中取值
        // 如果取出來(lái)的 bucket_t 的 _key = 0善延,說(shuō)明在索引的位置上還沒(méi)有緩存過(guò)方法少态,返回該 bucket_t,中止緩存查詢易遣,用于 cache_fill_nolock() 函數(shù)
        // 如果取出來(lái)的 bucket_t 的 _key = k彼妻,說(shuō)明查詢成功,返回該 bucket_t
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
      // 在 __arm64__ 下將索引 i -1豆茫,繼續(xù)查找澳骤,反向遍歷 _buckets 哈希表
      // 直到 i 指向首個(gè)元素即索引 = 0 時(shí),將 mask 賦值給 i澜薄,使其指向哈希表最后一個(gè)元素,繼續(xù)反向遍歷
      // 如果此時(shí)還沒(méi)有找到 k 對(duì)應(yīng)的 bucket_t 摊册,或者是空的 bucket_t 肤京,則循環(huán)結(jié)束,查找失敗茅特,調(diào)用 bad_cache() 函數(shù)
      // 接下來(lái)去類對(duì)象中 class_rw_t 中的 methods 查找
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)k, cls);
}


static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}
static inline mask_t cache_next(mask_t i, mask_t mask) {
    // return (i+1) & mask;  // __arm__  ||  __x86_64__  ||  __i386__
    return i ? i-1 : mask;   // __arm64__
}

2.2.2 緩存添加流程

//objc-cache.mm(objc4)
static void cache_fill_nolock(Class cls, SEL sel, IMP imp, id receiver)
{
    cacheUpdateLock.assertLocked();

    // Never cache before +initialize is done
    if (!cls->isInitialized()) return;   // 如果類還未初始化忘分,直接返回

    // Make sure the entry wasn't added to the cache by some other thread 
    // before we grabbed the cacheUpdateLock.
    if (cache_getImp(cls, sel)) return;  // 可能有其它線程搶先將該方法緩存了,所以要檢查一次緩存白修,如果存在妒峦,直接返回

    cache_t *cache = getCache(cls);  // ??取出該 class 的 cache_t
    cache_key_t key = getKey(sel);   // ??根據(jù) sel 獲得 _key

    // Use the cache as-is if it is less than 3/4 full
    mask_t newOccupied = cache->occupied() + 1;  // 將 cache_t 的 _occupied 即已經(jīng)緩存的方法數(shù)量 + 1,這里只是為了判斷 +1 后緩存容量是否滿
    mask_t capacity = cache->capacity();  // 獲得緩存容量 = _mask + 1
    if (cache->isConstantEmptyCache()) {  // 如果緩存是只讀的兵睛,重新申請(qǐng)緩存空間
        // Cache is read-only. Replace it.
        cache->reallocate(capacity, capacity ?: INIT_CACHE_SIZE);  // 申請(qǐng)新的緩存空間肯骇,并釋放舊的
    }
    else if (newOccupied <= capacity / 4 * 3) {  // ??如果當(dāng)前已經(jīng)緩存的方法數(shù)量 +1 <= 緩存容量的 3/4,就繼續(xù)往下操作
        // Cache is less than 3/4 full. Use it as-is.
    }
    else {  // ??如果以上條件不滿足祖很,說(shuō)明緩存已滿笛丙,進(jìn)行緩存擴(kuò)容
        // Cache is too full. Expand it.
        cache->expand();
    }

    // Scan for the first unused slot and insert there.     // 掃描第一個(gè)未使用的插槽(bucket_t)并將其插入
    // There is guaranteed to be an empty slot because the  // 必然會(huì)有一個(gè)空的插槽(bucket_t)
    // minimum size is 4 and we resized at 3/4 full.        // 因?yàn)樽钚〈笮∈?,我們調(diào)整為3/4滿
    bucket_t *bucket = cache->find(key, receiver);       // ??調(diào)用 find() 函數(shù)進(jìn)行一次緩存查找假颇,必然會(huì)得到一個(gè)空的 bucket_t
    if (bucket->key() == 0) cache->incrementOccupied();  // ??如果該 bucket_t 為空胚鸯,將 _occupied 即已經(jīng)緩存的方法數(shù)量 + 1
    bucket->set(key, imp);  // ??添加緩存
}

void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
    mutex_locker_t lock(cacheUpdateLock);
    cache_fill_nolock(cls, sel, imp, receiver);
#else
    _collecting_in_critical();
    return;
#endif
}

2.2.3 緩存擴(kuò)容流程

  • ① 設(shè)置新的緩存bucket_t,容量 = 舊的兩倍笨鸡;
  • ② 設(shè)置新的_mask=bucket_t長(zhǎng)度 - 1姜钳;
  • ③ 釋放舊的緩存(在runtime動(dòng)態(tài)交換方法實(shí)現(xiàn)時(shí)也會(huì)釋放緩存)坦冠。
//objc-cache.mm(objc4)
void cache_t::expand()
{
    cacheUpdateLock.assertLocked();
    
    uint32_t oldCapacity = capacity();
    // ??將緩存擴(kuò)容為原來(lái)的兩倍,如果是首次調(diào)用哥桥,設(shè)置緩存容量的初始值為 4
    uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;

    if ((uint32_t)(mask_t)newCapacity != newCapacity) {
        // mask overflow - can't grow further
        // fixme this wastes one bit of mask
        newCapacity = oldCapacity;
    }

    reallocate(oldCapacity, newCapacity);  // ??申請(qǐng)新的緩存空間辙浑,并釋放舊的
}

enum {
    INIT_CACHE_SIZE_LOG2 = 2,
    INIT_CACHE_SIZE      = (1 << INIT_CACHE_SIZE_LOG2)
};

void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
{
    bool freeOld = canBeFreed();  // ??判斷一下緩存是不是空的,如果為空泰讽,就沒(méi)必要釋放空間

    bucket_t *oldBuckets = buckets();
    bucket_t *newBuckets = allocateBuckets(newCapacity);

    // Cache's old contents are not propagated. 
    // This is thought to save cache memory at the cost of extra cache fills.
    // fixme re-measure this

    assert(newCapacity > 0);
    assert((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);

    setBucketsAndMask(newBuckets, newCapacity - 1);
    
    if (freeOld) {
        cache_collect_free(oldBuckets, oldCapacity);
        cache_collect(false);
    }
}

bool cache_t::canBeFreed()
{
    return !isConstantEmptyCache();
}

bool cache_t::isConstantEmptyCache()
{
    return 
        occupied() == 0  &&  
        buckets() == emptyBucketsForCapacity(capacity(), false);
}

3. isa 指針

  • isa指針用來(lái)維護(hù)對(duì)象和類之間的關(guān)系,并確保對(duì)象和類能夠通過(guò)isa指針找到對(duì)應(yīng)的方法已卸、實(shí)例變量佛玄、屬性、協(xié)議等累澡;
  • 在 arm64 架構(gòu)之前梦抢,isa就是一個(gè)普通的指針,直接指向objc_class愧哟,存儲(chǔ)著Class奥吩、Meta-Class對(duì)象的內(nèi)存地址。instance對(duì)象的isa指向class對(duì)象蕊梧,class對(duì)象的isa指向meta-class對(duì)象霞赫;
  • 從 arm64 架構(gòu)開(kāi)始,對(duì)isa進(jìn)行了優(yōu)化肥矢,變成了一個(gè)共用體(union)結(jié)構(gòu)端衰,還使用位域來(lái)存儲(chǔ)更多的信息。將 64 位的內(nèi)存數(shù)據(jù)分開(kāi)來(lái)存儲(chǔ)著很多的東西甘改,其中的 33 位才是拿來(lái)存儲(chǔ)class旅东、meta-class對(duì)象的內(nèi)存地址信息。要通過(guò)位運(yùn)算將isa的值& ISA_MASK掩碼十艾,才能得到class抵代、meta-class對(duì)象的內(nèi)存地址。
struct objc_object {
    Class isa;  // 在 arm64 架構(gòu)之前
};

struct objc_object {
private:
    isa_t isa;  // 在 arm64 架構(gòu)開(kāi)始
};

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

#if SUPPORT_PACKED_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // nonpointer must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL  // 用來(lái)取出 Class忘嫉、Meta-Class 對(duì)象的內(nèi)存地址
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1;  // 0:代表普通的指針荤牍,存儲(chǔ)著 Class、Meta-Class 對(duì)象的內(nèi)存地址
                                          // 1:代表優(yōu)化過(guò)榄融,使用位域存儲(chǔ)更多的信息
        uintptr_t has_assoc         : 1;  // 是否有設(shè)置過(guò)關(guān)聯(lián)對(duì)象参淫,如果沒(méi)有,釋放時(shí)會(huì)更快
        uintptr_t has_cxx_dtor      : 1;  // 是否有C++的析構(gòu)函數(shù)(.cxx_destruct)愧杯,如果沒(méi)有涎才,釋放時(shí)會(huì)更快
        uintptr_t shiftcls          : 33; // 存儲(chǔ)著 Class、Meta-Class 對(duì)象的內(nèi)存地址信息
        uintptr_t magic             : 6;  // 用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
        uintptr_t weakly_referenced : 1;  // 是否有被弱引用指向過(guò),如果沒(méi)有耍铜,釋放時(shí)會(huì)更快
        uintptr_t deallocating      : 1;  // 對(duì)象是否正在釋放
        uintptr_t has_sidetable_rc  : 1;  // 如果為1邑闺,代表引用計(jì)數(shù)過(guò)大無(wú)法存儲(chǔ)在 isa 中,那么超出的引用計(jì)數(shù)會(huì)存儲(chǔ)在一個(gè)叫 SideTable 結(jié)構(gòu)體的 RefCountMap(引用計(jì)數(shù)表)哈希表中
        uintptr_t extra_rc          : 19; // 里面存儲(chǔ)的值是引用計(jì)數(shù) retainCount - 1
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };
};

3.1 isa 與 superclass 指針指向

isa 與 superclass 指針指向

3.2 類對(duì)象(class)與元類對(duì)象(meta-class)

  • class棕兼、meta-class底層結(jié)構(gòu)都是objc_class結(jié)構(gòu)體陡舅,objc_class繼承自objc_object,所以它也有isa指針伴挚,所以它也是對(duì)象靶衍;
  • class中存儲(chǔ)著實(shí)例方法、成員變量茎芋、屬性颅眶、協(xié)議等信息,
    meta-class中存儲(chǔ)著類方法等信息田弥;
  • isa指針和superclass指針的指向(如上圖)涛酗;
  • 基類的meta-classsuperclass指向基類的class
    決定了一個(gè)性質(zhì):當(dāng)我們調(diào)用一個(gè)類方法偷厦,會(huì)通過(guò)classisa指針找到meta-class商叹,在meta-class中查找有無(wú)該類方法,如果沒(méi)有只泼,再通過(guò)meta-classsuperclass指針逐級(jí)查找父meta-class剖笙,一直找到基類的meta-class如果還沒(méi)找到該類方法的話,就會(huì)去找基類的class中同名的實(shí)例方法的實(shí)現(xiàn)请唱。

3.3 獲得 class 或者 meta-class 的方式

  • 獲得 class 有 3 種方式
- (Class)class;
+ (Class)class;
Class object_getClass(id obj);  // 傳參:instance 對(duì)象
  • 獲得 meta-class 只有 1 種方式
Class object_getClass(id obj);  // 傳參:Class 對(duì)象

示例代碼如下

    NSObject *object1 = [NSObject alloc] init];
    NSObject *object2 = [NSObject alloc] init];
    // objectClass1 ~ objectClass5 都是 NSObject 的類對(duì)象
    Class objectClass1 = [object1 class];
    Class objectClass2 = [object2 class];
    Class objectClass3 = [NSObject class];
    Class objectClass4 = object_getClass(object1);
    Class objectClass5 = object_getClass(object2);  
    // objectMetaClass1 ~ objectMetaClass4 都是 NSObject 的元類對(duì)象
    Class objectMetaClass1 = object_getClass([object1 class];    
    Class objectMetaClass2 = object_getClass([NSObject class]);    
    Class objectMetaClass3 = object_getClass(object_getClass(object1));    
    Class objectMetaClass4 = object_getClass(objectClass5);    

方法實(shí)現(xiàn)

- (Class)class {
    return object_getClass(self);
}

+ (Class)class {
    return self;
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();
    ......
}
objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}
#if __ARM_ARCH_7K__ >= 2
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif

3.4 為什么要設(shè)計(jì) meta-class 枯途?

目的是將實(shí)例和類的相關(guān)方法列表以及構(gòu)建信息區(qū)分開(kāi)來(lái),方便各司其職籍滴,符合單一職責(zé)設(shè)計(jì)原則。

4. method_t

  • Method就是method_t類型的指針榴啸;
  • method_t是對(duì)方法/函數(shù)的封裝(函數(shù)四要素:函數(shù)名孽惰、返回值、參數(shù)鸥印、函數(shù)體)勋功。
typedef struct method_t *Method;
struct method_t {
    SEL name;  // 方法名
    const char *types;  // 編碼(返回值類型、參數(shù)類型)
    IMP imp;   // 方法的地址/實(shí)現(xiàn)
};

4.1 SEL

  • SEL 又稱“選擇器”库说,它是一個(gè)指向方法的selector的指針狂鞋,代表方法/函數(shù)名;
  • SEL 維護(hù)在一個(gè)全局的 Map 中潜的,所以它是全局唯一的骚揍,不同類中相同名字的方法的 SEL 是相同的。
typedef struct objc_selector *SEL;
  • SEL 可以通過(guò)以下方式獲得
    SEL sel1 = @selector(selector);
    SEL sel2 = sel_registerName("selector");
    SEL sel3 = NSSelectorFromString(@"selector");
  • SEL 可以通過(guò)以下方式轉(zhuǎn)換成字符串
    char *string1 = sel_getName(sel1);
    NSString *string2 = NSStringFromSelector(sel1);

4.2 IMP

  • IMP 是指向方法實(shí)現(xiàn)的函數(shù)指針;
  • 我們調(diào)用方法信不,實(shí)際上就是根據(jù)方法 SEL 查找 IMP嘲叔;
  • method_t實(shí)際上相當(dāng)于在 SEL 和 IMP 之間做了一個(gè)映射。
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

4.3 Type Encodings

  • Type Encodings 編碼技術(shù)就是配合runtime的技術(shù)抽活,把一個(gè)方法的返回值類型硫戈、參數(shù)類型通過(guò)字符串的形式描述;
  • @encode()指令可以將類型轉(zhuǎn)換為 Type Encodings 字符串編碼下硕,
    @encode(int)=i丁逝;
  • OC方法都有兩個(gè)隱式參數(shù),方法調(diào)用者(id)self和方法名(SEL) _cmd梭姓,所以我們才能在方法中使用self_cmd霜幼;
  • -(void)test,它的編碼為“v16@0:8”糊昙,可以簡(jiǎn)寫為“v@:
    v:代表返回值類型為 void
    @:代表參數(shù) 1 類型為 id
    ::代表參數(shù) 2 類型為 SEL
    16:代表所有參數(shù)所占的總字節(jié)數(shù)
    0:代表參數(shù) 1 從第幾個(gè)字節(jié)開(kāi)始存儲(chǔ)
    8:代表參數(shù) 2 從第幾個(gè)字節(jié)開(kāi)始存儲(chǔ)
  • 下圖為類型對(duì)應(yīng)的 Type Encodings 編碼:
Objective-C type encodings
  • Type Encodings 在runtime的消息轉(zhuǎn)發(fā)中會(huì)使用到辛掠;
  • 更多關(guān)于 Type Encodings 的內(nèi)容,可以查看官方文檔 Type Encodings释牺。

?

下一篇

深入淺出 Runtime(三):消息機(jī)制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萝衩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子没咙,更是在濱河造成了極大的恐慌猩谊,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祭刚,死亡現(xiàn)場(chǎng)離奇詭異牌捷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)涡驮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門暗甥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捉捅,你說(shuō)我怎么就攤上這事撤防。” “怎么了棒口?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵寄月,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我无牵,道長(zhǎng)漾肮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任茎毁,我火速辦了婚禮克懊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己保檐,他們只是感情好耕蝉,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著夜只,像睡著了一般垒在。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扔亥,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天场躯,我揣著相機(jī)與錄音,去河邊找鬼旅挤。 笑死踢关,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粘茄。 我是一名探鬼主播签舞,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柒瓣!你這毒婦竟也來(lái)了儒搭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芙贫,失蹤者是張志新(化名)和其女友劉穎搂鲫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體磺平,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡魂仍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拣挪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擦酌。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖菠劝,靈堂內(nèi)的尸體忽然破棺而出仑氛,到底是詐尸還是另有隱情,我是刑警寧澤闸英,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站介袜,受9級(jí)特大地震影響甫何,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遇伞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一辙喂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦巍耗、人聲如沸秋麸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灸蟆。三九已至,卻和暖如春亲族,著一層夾襖步出監(jiān)牢的瞬間炒考,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工霎迫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斋枢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓知给,卻偏偏與公主長(zhǎng)得像瓤帚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涩赢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容