Objective-C Runtime淺析

  • 前言
  • Runtime是什么
  • Runtime的實(shí)現(xiàn)原理
    • 消息傳遞機(jī)制
    • Runtime基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
      • NSObject & id
      • objc_object
      • Class
      • 元類(Meta Class)
      • Category
      • Ivar
      • Method
    • SEL與IMP的區(qū)別是什么? IMP如何尋址蝶怔?
    • Category與Extension的區(qū)別丧失?
  • Runtime的使用

前言

編譯型的語(yǔ)言都需要經(jīng)過編譯之后再運(yùn)行布朦,OC編譯器部分由Clang+ LLVM組成,編譯的過程通常包括:預(yù)處理(Preprocessor)撩银、編譯(Compiler)、匯編(Assembler)、鏈接(Linker)這幾個(gè)過程荷愕,然后才是加載運(yùn)行。

預(yù)處理: 簡(jiǎn)化代碼棍矛,過濾注釋安疗,處理宏定義(#開頭關(guān)鍵字一般都和預(yù)處理有關(guān));
編譯:將預(yù)處理之后的文件編譯成匯編語(yǔ)言文件够委;
匯編:將匯編文件轉(zhuǎn)成機(jī)器可識(shí)別指令荐类;
鏈接:將文件鏈接成可以執(zhí)行的程序。

實(shí)際上編譯的過程涉及很多繁瑣復(fù)雜的內(nèi)容茁帽,感興趣的話可以看看編譯原理玉罐。
正常情況下,靜態(tài)語(yǔ)言潘拨,比如C語(yǔ)言吊输,會(huì)在編譯階段確定數(shù)據(jù)類型,函數(shù)邏輯等等铁追,從main()開始自上而下執(zhí)行季蚂,這個(gè)過程中你將無(wú)法再修改執(zhí)行的函數(shù),事實(shí)上琅束,這也正是C語(yǔ)言被稱為面向過程的原因癣蟋,你需要通過邏輯控制執(zhí)行過程。
Objective-C是一門動(dòng)態(tài)語(yǔ)言狰闪,它將從編譯到鏈接執(zhí)行的內(nèi)容推遲到了實(shí)際運(yùn)行之前決定疯搅。Objective-C通過消息傳遞機(jī)制確定執(zhí)行的類型和方法,通過其運(yùn)行時(shí)機(jī)制埋泵,你可以將消息重定向給適當(dāng)?shù)膶?duì)象幔欧,甚至還可以交換方法的實(shí)現(xiàn)罪治。Objective-C具有動(dòng)態(tài)性都要?dú)w功于Runtime

Runtime是什么

Runtime是蘋果公司設(shè)計(jì)的支持Objective-C動(dòng)態(tài)性的庫(kù),Runtime是主要由C語(yǔ)言編寫的礁蔗,通過這個(gè)庫(kù)為Objective-C言語(yǔ)添加了面向?qū)ο蟮奶匦院蛣?dòng)態(tài)機(jī)制觉义。所有的OC程序都會(huì)鏈接到Runtime庫(kù),它提供了動(dòng)態(tài)類型浴井、動(dòng)態(tài)加載晒骇、動(dòng)態(tài)綁定等一系列基礎(chǔ)。

Runtime的實(shí)現(xiàn)

消息傳遞機(jī)制

Objective-C面向?qū)ο蟮膶?shí)現(xiàn)是繼承于Smalltalk的磺浙,即將所有的東西都當(dāng)作對(duì)象洪囤,通過向?qū)ο蟀l(fā)送消息來執(zhí)行程序。例如:

[self doSomthing: var1];

上面調(diào)用方法撕氧,會(huì)被編譯器轉(zhuǎn)化為C語(yǔ)言的:

objc_msgSend(self, @selector(doSomething:), var1);

即向self(消息的接收者)瘤缩,發(fā)送@selector(doSomething:), var1是傳遞的參數(shù)伦泥。
對(duì)象self收到消息之后剥啤,具體調(diào)用哪個(gè)方法則會(huì)在運(yùn)行時(shí)決定。換句話說不脯,Objective-C在編譯時(shí)并沒有真正的連接要調(diào)用的方法府怯,而是通過發(fā)送消息給對(duì)象的方式,在運(yùn)行時(shí)去連接調(diào)用的方法防楷。這也就是實(shí)現(xiàn)Objective-C動(dòng)態(tài)性的核心原理牺丙。
而對(duì)象接收消息后又是如何連接到方法的?回答這個(gè)問題之前域帐,我們不得不先看一下Runtime中是如何定義類赘被,對(duì)象的。

Runtime基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

下面的定義都可以在Runtime源碼Runtime源碼中看到肖揣,源碼中可以找到Class , NSObject, Protocol, Category, SEL等等概念的結(jié)構(gòu)民假,下面我們一一分析。

NSObject & id

我們使用的大多數(shù)類都是繼承自NSObject龙优,那我們可以先查看下NSObject.h.

@protocol NSObject

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;

@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'anObject.dynamicType' instead");
- (instancetype)self;

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
...


@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
+ (void)load;

+ (void)initialize;
- (instancetype)init
...

從以上代碼羊异,可以看到NSObject類是遵循NSObject協(xié)議的,并且其中包含一個(gè)特殊的Class類型的成員變量isa, 這個(gè)isa其實(shí)是一個(gè)指向objc_class的指針彤断,也就是指向自己類的指針野舶。

如果繼續(xù)查看objc.h, 我們還會(huì)發(fā)現(xiàn)特殊類型id的定義:

/// A pointer to an instance of a class.
typedef struct objc_object *id;

所以id就是一個(gè)指向類實(shí)例的指針,所以我們可以用id類型來指代不確定類型的實(shí)例宰衙。 下面繼續(xù)看objc_object.

objc_object

這部分代碼是在objc-private.h中:

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();
    ...

私有成員isa是一個(gè)union類型平道,C語(yǔ)言的union類型表示幾個(gè)變量公用一個(gè)內(nèi)存位置, 在不同的時(shí)間保存不同的數(shù)據(jù)類型和不同長(zhǎng)度的變量。在arm64供炼,Objective-C2.0下一屋, isa的作用不僅僅是指向一個(gè)類實(shí)例的指針窘疮,它包含了引用計(jì)數(shù)、析構(gòu)狀態(tài)冀墨、關(guān)聯(lián)對(duì)象闸衫、weak引用等等更多地信息。

isa_t源碼定義如下:

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

    Class cls;    
    uintptr_t bits;

#if SUPPORT_NONPOINTER_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // indexed 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
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t indexed           : 1;  //0 表示普通的 isa 指針诽嘉,1 表示使用優(yōu)化蔚出,存儲(chǔ)引用計(jì)數(shù)
        uintptr_t has_assoc         : 1;  //表示該對(duì)象是否包含 associated object,如果沒有虫腋,則析構(gòu)時(shí)會(huì)更快
        uintptr_t has_cxx_dtor      : 1;  //表示該對(duì)象是否有 C++ 或 ARC 的析構(gòu)函數(shù)骄酗,如果沒有,則析構(gòu)時(shí)更快
        uintptr_t shiftcls          : 33; //類的指針
        uintptr_t magic             : 6;  //固定值為 0xd2岔乔,用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化酥筝。
        uintptr_t weakly_referenced : 1;  //表示該對(duì)象是否有過 weak 對(duì)象滚躯,如果沒有雏门,則析構(gòu)時(shí)更快
        uintptr_t deallocating      : 1;  //表示該對(duì)象是否正在析構(gòu)
        uintptr_t has_sidetable_rc  : 1;  //表示該對(duì)象的引用計(jì)數(shù)值是否過大無(wú)法存儲(chǔ)在 isa 指針
        uintptr_t extra_rc          : 19; //存儲(chǔ)引用計(jì)數(shù)值減一后的結(jié)果
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    };
...

  #if __OBJC2__
  typedef struct method_t *Method;
  typedef struct ivar_t *Ivar;
  typedef struct category_t *Category;
  typedef struct property_t *objc_property_t;
};
....

SUPPORT_NONPOINTER_ISA宏定義表名這個(gè)isa不再只是指向類的指針,而是經(jīng)過優(yōu)化掸掏,包含更多信息茁影。
其中clsobjc_class結(jié)構(gòu)體指針類型,bits可以操作整個(gè)內(nèi)存區(qū)丧凤,下面的結(jié)構(gòu)體聲明位域募闲,上面只復(fù)制了arm64環(huán)境下的代碼。
在Objective-C2.0時(shí)愿待,對(duì)于Method, Category, Ivar, 屬性進(jìn)行了新的定義浩螺,后面我們一一來看這些結(jié)構(gòu)體。

另外仍侥,

Class

看源碼中的定義要出,Classobjc_class *,而objc_class繼承自objc_object农渊。(以前objc_class是單獨(dú)定義的, Objective-C2.0做了修改)患蹂。

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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }   
    ... 
  1. superclass指向父類。
  2. cache處理已調(diào)用方法的緩存砸紊。
  3. bits存儲(chǔ)class_rw_t地址传于,并且定義了一些基本操作。
    class_rw_t 是非常重要的一個(gè)結(jié)構(gòu)體醉顽,定義如下:
struct class_rw_t {
    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;
    ...

這里定義了方法沼溜、屬性和協(xié)議列表,但是注意常量ro,是一個(gè)class_ro_t結(jié)構(gòu)體指針,其定義如下:

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#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;
    }
};

其中也定義了帶著"base"的方法游添、屬性和協(xié)議列表系草,它們之間是什么關(guān)系呢弹惦?
我也十分困惑,但在這里找到了一些答案悄但。編譯class時(shí), objc_classbits是指向ro的棠隐,然后創(chuàng)建rw時(shí)將bits里的ro賦值給rw,作為它的一個(gè)常量檐嚣,最后bitsrw替換原先存儲(chǔ)的ro助泽。
當(dāng)然,類初始化后嚎京,這些屬性嗡贺、方法和協(xié)議列表都是空的,運(yùn)行時(shí)會(huì)動(dòng)態(tài)添加修改鞍帝。

元類(Meta Class)

為了引出元類的概念诫睬,我們不得不重點(diǎn)提一下objc_classisa,這是從objc_object繼承下來的帕涌。一個(gè)對(duì)象實(shí)例的方法是通過isa結(jié)構(gòu)體保存的摄凡,那么一個(gè)類的類方法保存在哪?這就是objc_classisa存在的意義蚓曼。
一個(gè)objc_classisa所指向的類實(shí)例亲澡,我們稱之為元類(Meta Class)∪野妫看下面objc_class中判斷元類和獲取元類的幾個(gè)方法:

    bool isMetaClass() {
        assert(this);
        assert(isRealized());
        return data()->ro->flags & RO_META;
    }

    // NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

    bool isRootClass() {
        return superclass == nil;
    }
    bool isRootMetaclass() {
        return ISA() == (Class)this;
    }

isMetaClass的語(yǔ)句data()->ro->flags & RO_META, 表面這個(gè)flag字段是標(biāo)識(shí)一個(gè)class是否是元類的床绪。getMeta()是獲取一個(gè)類的元類的方法,一個(gè)類如果是元類其弊,它的元類會(huì)指向它本身癞己,如果是一個(gè)普通類,則它的元類是指向同類型的類梭伐。
我們也引入一個(gè)經(jīng)典的圖片來做解釋:

class diagram.png

一個(gè)類的元類的父類痹雅,是其父類的元類,通常NSObject類是Root class(根類), 其superclass指向nil籽御, 其元類也是NSObject類练慕,而Root Meta class(根元類)的父類則指向NSObject(根類),這樣形成一個(gè)閉環(huán)技掏。

Category

Category就是我們常用的類別铃将,查看源碼定義,我們可以通過類別向已存在的類添加實(shí)例方法哑梳,實(shí)例屬性劲阎,類方法,協(xié)議鸠真。
當(dāng)Application啟動(dòng)時(shí)悯仙,加載類時(shí)會(huì)調(diào)用attachCategories(Class cls, category_list *cats, bool flush_caches)方法按類別加載順序向類追加所有Category的相關(guān)內(nèi)容龄毡。

/// An opaque type that represents a category.
typedef struct objc_category *Category;
struct category_t {
    const char *name;   // 類別名
    classref_t cls;     // 所屬類
    struct method_list_t *instanceMethods;  // 添加的實(shí)例方法列表
    struct method_list_t *classMethods;     // 添加的類方法列表
    struct protocol_list_t *protocols;      // 添加的協(xié)議列表
    struct property_list_t *instanceProperties; // 添加的實(shí)例屬性

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta) {
        if (isMeta) return nil; // classProperties;
        else return instanceProperties;
    }
};

關(guān)于Category方法調(diào)用覆蓋問題,需要知道的是Category的方法是在運(yùn)行時(shí)追加到類的方法列表頂部锡垄,IMP查找順序:Category->Class->Super Class, 當(dāng)在多個(gè)Category中定義同名方法時(shí)沦零,當(dāng)查找到第一個(gè)IMP時(shí),就會(huì)立即返回货岭,而不會(huì)繼續(xù)查找路操,也就是會(huì)“覆蓋”后面的方法。

Ivar

Ivar 代表類的實(shí)例對(duì)象千贯。

typedef struct objc_ivar *Ivar;
Method

方法的定義屯仗,引出兩個(gè)重要的概念,方法名標(biāo)志類型SEL和入口指針類型IMP搔谴。

typedef struct method_t *Method;
struct method_t {
    SEL name;
    const char *types;
    IMP imp;

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};
SEL
typedef struct objc_selector *SEL;

SEL本質(zhì)是映射到方法的C字符串魁袜,也就是SEL字符串包含了方法名、參數(shù)等敦第,它是方法選擇器(selector)在Objc中的表示類型峰弹。

IMP
typedef id (*IMP)(id, SEL, ...);

IMP是個(gè)函數(shù)指針,它指向的函數(shù)體就是對(duì)象接收消息后最終執(zhí)行的代碼申尼。


SEL與IMP的區(qū)別是什么垮卓?IMP如何尋址垫桂?

SEL是方法選擇器师幕,它其實(shí)保存的是方法編號(hào),而IMP是指向函數(shù)地址的指針诬滩,方法編號(hào)與方法地址一一對(duì)應(yīng)存儲(chǔ)在類的isa的dispatch table中霹粥。

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    imp = lookUpImpOrNil(cls, sel, nil, 
                         YES/*initialize*/, YES/*cache*/, YES/*resolver*/);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

上面是獲取方法實(shí)現(xiàn)的函數(shù),根據(jù)類和SEL來查找IMP疼鸟,如果clssel傳入的是nil, 直接返回nil, 否則通過IMP lookUpImpOrNil(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)后控。繼續(xù)查找:

IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 
                   bool initialize, bool cache, bool resolver)
{
    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
    if (imp == _objc_msgForward_impcache) return nil;
    else return imp;
}

這個(gè)方法主要還是調(diào)用lookUpImpOrForward方法,只不過用nil替換_objc_msgForward_impcache空镜。
終于找到核心實(shí)現(xiàn)的代碼浩淘,我們看看lookUpImpOrForward方法的實(shí)現(xiàn), 我將部分解釋放在了注釋里吴攒。


/***********************************************************************
* 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  如果使用了 Optimistic cache张抄,會(huì)先從這個(gè)cache中查找
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    if (!cls->isRealized()) {
        rwlock_writer_t lock(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();

    // Ignore GC selectors如果是忽略方法,goto執(zhí)行done部分
    if (ignoreSelector(sel)) {
        imp = _objc_ignored_method;
        cache_fill(cls, sel, imp, inst);
        goto done;
    }

    // Try this class's cache. 從本類的緩存中查找(前面提過洼怔,一個(gè)類的在程序執(zhí)行過的方法署惯,會(huì)被添加到其cache_t中,這樣再次調(diào)用時(shí)這個(gè)方法時(shí)會(huì)加快速度镣隶。)

    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists. 從本類的方法列表查找

    meth = getMethodNoSuper_nolock(cls, sel);
    if (meth) {
        log_and_fill_cache(cls, meth->imp, sel, inst, cls);
        imp = meth->imp;
        goto done;
    }

    // Try superclass caches and method lists.從父類的緩存中查找

    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;
            }
        }

        // Superclass method list.從父類的方法列表查找
        meth = getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
            imp = meth->imp;
            goto done;
        }
    }

    // No implementation found. Try method resolver once. 從運(yùn)行時(shí)添加的方法中去查找

    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.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlockRead();

    // paranoia: look for ignored selectors with non-ignored implementations
    assert(!(ignoreSelector(sel)  &&  imp != (IMP)&_objc_ignored_method));

    // paranoia: never let uncached leak out
    assert(imp != _objc_msgSend_uncached_impcache);

    return imp;
}

總結(jié)一下查找流程:

  1. Optimistic cache 查找
  2. 忽略的方法中查找
  3. 當(dāng)前類的緩存中查找
  4. 當(dāng)前類的方法列表中查找
  5. 父類的緩存中查找
  6. 父類的方法列表中查找
  7. 動(dòng)態(tài)添加的方法找查找

Category 和 Extension 在Objective-C中的區(qū)別

1极谊,Category是運(yùn)行時(shí)加載的诡右,Category中的方法是動(dòng)態(tài)添加到類的方法列表的。Extension是類的擴(kuò)展轻猖,擴(kuò)展中的方法會(huì)在編譯時(shí)添加的帆吻。
2,類的成員變量在編譯后不可變的咙边,而方法列表是可變的桅锄,所以我們可以在Extension中定義成員變量,但是在Category中不可以样眠。為Category添加屬性時(shí)友瘤,我們也只能通過運(yùn)行時(shí)實(shí)現(xiàn)屬性的Setter和getter方法。(通過objc_setAssociatedObjectobjc_getAssociatedObject這兩個(gè)方法)檐束。
3辫秧, 我們可以為系統(tǒng)類型添加Category,但是不能為其添加Extension被丧,因?yàn)镋xtension只能局限于類的實(shí)現(xiàn)中盟戏。

Runtime的使用

Objc 從三種不同的層級(jí)上與 Runtime 系統(tǒng)進(jìn)行交互,分別是通過 Objective-C 源代碼甥桂,通過 Foundation 框架的NSObject類定義的方法柿究,通過對(duì) runtime 函數(shù)的直接調(diào)用。

展開理解一下這三個(gè)層級(jí):
Objective-C 源代碼:OC源碼底層都是需要和Runtime交互的黄选,我們已經(jīng)知道OC的類蝇摸、方法、協(xié)議等等都是在Runtime中通過數(shù)據(jù)結(jié)構(gòu)定義的办陷,可以說我們所有的代碼都是在Runtime上編寫的貌夕。
NSObject類 : 我們?cè)谡{(diào)用isKindOfClass:respondsToSelector民镜,conformsToProtocol等等類型檢查啡专、方法檢查時(shí),都是在運(yùn)行時(shí)進(jìn)行的制圈,這是從NSObject類的層面上们童,諸多方法是在使用Runtime。
Runtime的函數(shù): #import <objc/runtime.h>之后鲸鹦,我們可以直接調(diào)用Runtime開放的接口慧库,為我們的類動(dòng)態(tài)添加方法,交互方法實(shí)現(xiàn)等等亥鬓,比如MJRefresh完沪,就是使用Runtime的方法為scrollview添加刷新headerfooter
runtime.h中,runtime通過OBJC_EXPORT宏將部分方法暴露出來給開發(fā)者直接使用覆积,我們也可以查看蘋果官方文檔查看如何使用這部分API听皿。

參考文章:
Objective-C Runtime 這篇介紹的很系統(tǒng)、很詳細(xì)
iOS開發(fā)教程之Objc Runtime筆記 這篇也很不錯(cuò)宽档,剖析源碼尉姨,很深入。
Objective-C Runtime 蘋果文檔
Runtime源碼
元類Mate-Class
Objective-C引用計(jì)數(shù)原理
https://github.com/DeveloperErenLiu/RuntimePDF

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吗冤,一起剝皮案震驚了整個(gè)濱河市又厉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌椎瘟,老刑警劉巖覆致,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肺蔚,居然都是意外死亡煌妈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門宣羊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來璧诵,“玉大人,你說我怎么就攤上這事仇冯≈蓿” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵苛坚,是天一觀的道長(zhǎng)比被。 經(jīng)常有香客問我,道長(zhǎng)炕婶,這世上最難降的妖魔是什么姐赡? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮柠掂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘依沮。我一直安慰自己涯贞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布危喉。 她就那樣靜靜地躺著宋渔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辜限。 梳的紋絲不亂的頭發(fā)上皇拣,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼氧急。 笑死颗胡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吩坝。 我是一名探鬼主播毒姨,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼钉寝!你這毒婦竟也來了弧呐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嵌纲,失蹤者是張志新(化名)和其女友劉穎俘枫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逮走,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崩哩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了言沐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邓嘹。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖险胰,靈堂內(nèi)的尸體忽然破棺而出汹押,到底是詐尸還是另有隱情,我是刑警寧澤起便,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布棚贾,位于F島的核電站,受9級(jí)特大地震影響榆综,放射性物質(zhì)發(fā)生泄漏妙痹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一鼻疮、第九天 我趴在偏房一處隱蔽的房頂上張望怯伊。 院中可真熱鬧,春花似錦判沟、人聲如沸耿芹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吧秕。三九已至,卻和暖如春迹炼,著一層夾襖步出監(jiān)牢的瞬間砸彬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砂碉,地道東北人蛀蜜。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绽淘,于是被迫代替她去往敵國(guó)和親涵防。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉沪铭,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,715評(píng)論 0 9
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 734評(píng)論 0 2
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語(yǔ)言壮池,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,195評(píng)論 0 7
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí)杀怠,它使得 Objective-C 如虎添翼椰憋,具備了靈活的...
    lylaut閱讀 800評(píng)論 0 4
  • 文中的實(shí)驗(yàn)代碼我放在了這個(gè)項(xiàng)目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 923評(píng)論 0 6