OC底層之 消息發(fā)送機制

1悴务、編譯時

編譯階段,會通過編譯器將語言轉(zhuǎn)化成機器碼譬猫,然后直接在CPU上執(zhí)行機器碼惨寿,效率
更高.

OC便是使用編譯器進行處理,生成可執(zhí)行文件删窒;
而像Python等語言則是使用解釋器,在運行時解釋執(zhí)行代碼顺囊,將代碼翻譯成目標代
碼肌索,然后再一句一句的去執(zhí)行目標代碼,所以相對來說效率就低特碳,但是跑起來后不用
重啟編譯代碼诚亚,直接修改代碼就可以看到效果。

編譯過程的詳細信息請移步

2午乓、為什么說OC是動態(tài)語言

靜態(tài)語言站宗,是在編譯期已經(jīng)決定了一切,在運行時只是遵守在編譯器確定好的指令
在進行而已;
動態(tài)語言益愈,只是在編譯期做語義語法的檢查梢灭,而不會分配內(nèi)存,在編譯期只關(guān)心對
象是否調(diào)用這個方法蒸其,而不會關(guān)心這個對象是否為nil敏释, 也不會關(guān)心這個方法的具體
實現(xiàn)以及這個對象的本質(zhì)是否能調(diào)用該方法,更不會關(guān)心有沒有這個方法實現(xiàn)摸袁,這些
事情都是在運行時進行的.

OC的動態(tài)性體現(xiàn)在三個方面:動態(tài)類型钥顽,動態(tài)綁定動態(tài)加載

  • 動態(tài)類型,在運行時才確定對象的類型靠汁。如'NSString *data = [[NSData alloc] init]' 編譯時是字符串類型蜂大,運行時確定真正類型是data類型
  • 動態(tài)綁定闽铐,基于動態(tài)類型,在對象被確定后奶浦,其類型也被確定了兄墅,則該對象對應(yīng)的屬性和響應(yīng)小時也就確定了
  • 動態(tài)加載,根據(jù)需求加載所需資源财喳,讓程序在運行時添加代碼模塊以及其他資源, 用戶可以根據(jù)需要加載一些可執(zhí)行代碼和資源察迟,而不是在啟動時就加載所有組件

3、對象的底層結(jié)構(gòu)

isa指針耳高,實例對象通過它的isa指針找到它的類扎瓶,類對象通過它找到它的元類,元類指向根類
isa 指針確定所屬類泌枪,super_class 確定繼承關(guān)系概荷。

/// 實例對象的底層結(jié)構(gòu)
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// 類對象的底層結(jié)構(gòu)
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() const {
        return bits.data();
    }
};

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

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

/// 對象結(jié)構(gòu)
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    
    uintptr_t isaBits() const;

    // initIsa() should be used to init the isa of new objects only.
    // If this object already has an isa, use changeIsa() for correctness.
    // initInstanceIsa(): objects with no custom RR/AWZ
    // initClassIsa(): class objects
    // initProtocolIsa(): protocol objects
    // initIsa(): other objects
    void initIsa(Class cls /*nonpointer=false*/);
    void initClassIsa(Class cls /*nonpointer=maybe*/);
    void initProtocolIsa(Class cls /*nonpointer=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);

    // changeIsa() should be used to change the isa of existing objects.
    // If this is a new object, use initIsa() for performance.
    Class changeIsa(Class newCls);

    bool hasNonpointerIsa();
    bool isTaggedPointer();
    bool isBasicTaggedPointer();
    bool isExtTaggedPointer();
    bool isClass();

    // object may have associated objects?
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();

    // object may be weakly referenced?
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();

    // object may have -.cxx_destruct implementation?
    bool hasCxxDtor();

    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();

    // Implementations of retain/release methods
    id rootRetain();
    bool rootRelease();
    id rootAutorelease();
    bool rootTryRetain();
    bool rootReleaseShouldDealloc();
    uintptr_t rootRetainCount();

    // Implementation of dealloc methods
    bool rootIsDeallocating();
    void clearDeallocating();
    void rootDealloc();

private:
    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);

    // Slow paths for inline control
    id rootAutorelease2();
    uintptr_t overrelease_error();

#if SUPPORT_NONPOINTER_ISA
    // Unified retain count manipulation for nonpointer isa
    id rootRetain(bool tryRetain, bool handleOverflow);
    bool rootRelease(bool performDealloc, bool handleUnderflow);
    id rootRetain_overflow(bool tryRetain);
    uintptr_t rootRelease_underflow(bool performDealloc);

    void clearDeallocating_slow();

    // Side table retain count overflow for nonpointer isa
    void sidetable_lock();
    void sidetable_unlock();

    void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
    bool sidetable_addExtraRC_nolock(size_t delta_rc);
    size_t sidetable_subExtraRC_nolock(size_t delta_rc);
    size_t sidetable_getExtraRC_nolock();
#endif

    // Side-table-only retain count
    bool sidetable_isDeallocating();
    void sidetable_clearDeallocating();

    bool sidetable_isWeaklyReferenced();
    void sidetable_setWeaklyReferenced_nolock();

    id sidetable_retain();
    id sidetable_retain_slow(SideTable& table);

    uintptr_t sidetable_release(bool performDealloc = true);
    uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);

    bool sidetable_tryRetain();

    uintptr_t sidetable_retainCount();
#if DEBUG
    bool sidetable_present();
#endif
};

從上面的結(jié)構(gòu)體信息,可以知道碌燕,每個對象的本質(zhì)都是一個結(jié)構(gòu)體误证,都有一個isa指
針指向?qū)?yīng)的類

4、消息發(fā)送的基礎(chǔ)數(shù)據(jù)

SEL修壕,方法選擇器愈捅,是一個方法的selector指針,用于表示運行時下方法的名稱慈鸠,定義如下:

typedef struct objc_selector *SEL;

1蓝谨、底層維護一張選擇子表(SEL
2、OC編譯時青团,會依據(jù)每個方法的名稱和參數(shù)生成一個唯一整型標識(int類型的地
址)譬巫,這個標識就是SEL
3、兩個類之間督笆,不管有沒有關(guān)系芦昔,只要方法名相同,對應(yīng)的SEL地址是一樣的娃肿,所以
在OC同一個類(及類的繼承體系)中咕缎,不能存在2個同名的方法,即使參數(shù)類型不同也
不行(同一個類使用相同的方法名料扰,即便參數(shù)不同锨阿,編譯時也會報錯)
4、不同類方法名可以相同记罚,因為方法查找會根據(jù)SEL從各自的類方法列表中查詢對應(yīng)
IMP

/// SEL方法調(diào)用
// 返回給定選擇器指定的方法的名稱
const char * sel_getName ( SEL sel );

/* 
向Runtime系統(tǒng)中注冊一個方法墅诡,如果已注冊則直接返回對應(yīng)的選擇器,如果未注冊,
則將方法名映射到一個選擇器末早,并返回這個選擇器
*/
SEL sel_registerName ( const char *str );

// 在Objective-C Runtime系統(tǒng)中注冊一個方法
SEL sel_getUid ( const char *str );

// 比較兩個選擇器
BOOL sel_isEqual ( SEL lhs, SEL rhs );

IMP 函數(shù)指針烟馅,指向方法實現(xiàn)的首地址

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

Method 表示類定義中的方法

struct objc_method {
/// 方法名
    SEL _Nonnull method_name       OBJC2_UNAVAILABLE;
/// 方法類型
    char * _Nullable method_types  OBJC2_UNAVAILABLE;
/// 方法實現(xiàn)
    IMP _Nonnull method_imp        OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
/// Method 方法調(diào)用
// 調(diào)用指定方法的實現(xiàn),返回的是實際實現(xiàn)的返回值然磷。參數(shù)receiver不能為空郑趁。這個方法的效率會比method_getImplementation和method_getName更快
id method_invoke ( id receiver, Method m, ... );

// 調(diào)用返回一個數(shù)據(jù)結(jié)構(gòu)的方法的實現(xiàn)
void method_invoke_stret ( id receiver, Method m, ... );

// 獲取方法名
SEL method_getName ( Method m );

// 返回方法的實現(xiàn)
IMP method_getImplementation ( Method m );

// 獲取描述方法參數(shù)和返回值類型的字符串
const char * method_getTypeEncoding ( Method m );

// 獲取方法的返回值類型的字符串
char * method_copyReturnType ( Method m );

// 獲取方法的指定位置參數(shù)的類型字符串
char * method_copyArgumentType ( Method m, unsigned int index );

// 通過引用返回方法的返回值類型字符串
void method_getReturnType ( Method m, char *dst, size_t dst_len );

// 返回方法的參數(shù)的個數(shù)
unsigned int method_getNumberOfArguments ( Method m );

// 通過引用返回方法指定位置參數(shù)的類型字符串
void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len );

// 返回指定方法的方法描述結(jié)構(gòu)體
struct objc_method_description * method_getDescription ( Method m );

// 設(shè)置方法的實現(xiàn)
IMP method_setImplementation ( Method m, IMP imp );

// 交換兩個方法的實現(xiàn)
void method_exchangeImplementations ( Method m1, Method m2 );

5、消息發(fā)送機制

消息發(fā)送流程:

消息發(fā)送流程
  • 1姿搜、編譯器將消息發(fā)送轉(zhuǎn)化成objct_msgSend(id , SEL, ...)寡润,通過匯編代碼
    一步步處理最終會走到這個方法lookUpImpOrForward(最新的源碼)
  • 2、先判斷當期那對象是否為nil舅柜,是則直接返回nil
  • 3梭纹、在運行時去執(zhí)行時,該對象(實例或類)會先通過isa指針指向?qū)?yīng)的類(類或元
    類)
  • 4致份、通過SEL簽名在cache_t中進行查找变抽,有則返回
  • 5、沒有再去method_list中進行查找氮块,有則添加到緩存中,并回調(diào)
  • 6绍载、沒有就通過super_class找到父類,繼續(xù)進行查找滔蝉,一直循環(huán)遍歷击儡,直到根類NSObject
  • 7、以上都未能找到蝠引,則進行動態(tài)解析阳谍,最后進入消息轉(zhuǎn)發(fā)
異常提示IMP:
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
    _objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
                "(no message forward handler is installed)", 
                class_isMetaClass(object_getClass(self)) ? '+' : '-', 
                object_getClassName(self), sel_getName(sel), self);
}
/// 截取部分匯編源碼,可以看到lookUpImpOrForward調(diào)用的基本信息
    // lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
    // receiver and selector already in x0 and x1
    mov x2, x16
    mov x3, #3
    bl  _lookUpImpOrForward


IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    /* 
    _objc_msgForward_impcache 這里通過匯編發(fā)現(xiàn)最終執(zhí)行了
    objc_defaultForwardHandler方法調(diào)用立肘,主要用來進行異常回調(diào)
    */
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;

    IMP imp = nil;
    Class curClass;

    ///添加線程鎖名扛,防止多線程操作
    runtimeLock.assertUnlocked();

    //如果當前的行為是查找緩存谅年,直接查找當前方法緩存
    if (fastpath(behavior & LOOKUP_CACHE)) {
        imp = cache_getImp(cls, sel);
        if (imp) goto done_nolock;
    }
    runtimeLock.lock();

    /// 類的懶加載(為查找方法做準備條件,如果類沒有初始化時肮韧,初始化類和父類融蹂、元
    /// 類等),對是否有swift進行不同的處理
    if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
    }

/// 類的初始化操作
    if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
    }

    runtimeLock.assertLocked();
    curClass = cls;

/// 循環(huán)遍歷類-父類進行查找
    for (unsigned attempts = unreasonableClassCount();;) {
        Method meth = getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
        // 查找當前類中的方法列表弄企,如果方法存在超燃,則跳轉(zhuǎn)到緩存插入處理
            imp = meth->imp;
            goto done;
        }

        /// 對當前類進行重新賦值,查找父類
        if (slowpath((curClass = curClass->superclass) == nil)) {
            // No implementation found, and method resolver did not help.
            // Use forwarding.
            imp = forward_imp;
            break;
        }

        // Halt if there is a cycle in the superclass chain.
        if (slowpath(--attempts == 0)) {
            _objc_fatal("Memory corruption in class list.");
        }

        // Superclass cache.
        imp = cache_getImp(curClass, sel);
        if (slowpath(imp == forward_imp)) {
            // Found a forward:: entry in a superclass.
            // Stop searching, but do not cache yet; call method
            // resolver for this class first.
            break;
        }
        if (fastpath(imp)) {
            /// 查詢到父類的緩存中存在該方法拘领,則直接插入到當前類緩存中
            goto done;
        }
    }

    //這里進入消息轉(zhuǎn)發(fā)
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

 done:
/// 緩存插入
    log_and_fill_cache(cls, imp, sel, inst, curClass);
    runtimeLock.unlock();
 done_nolock:
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;
}
  • 從上面lookUpImpOrForward方法查找過程中意乓,我們可以看到對類或者父類方法列表
    進行方法查詢用到了getMethodNoSuper_nolock這個方法:
  • getMethodNoSuper_nolock,對search_method_list_inline進行遍歷,查詢對應(yīng)的方法
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    ASSERT(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    for (auto mlists = cls->data()->methods.beginLists(), 
              end = cls->data()->methods.endLists(); 
         mlists != end;
         ++mlists)
    {
        // <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
        // caller of search_method_list, inlining it turns
        // getMethodNoSuper_nolock into a frame-less function and eliminates
        // any store from this codepath.
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

  • search_method_list_inline,在所有方法列表中(自身约素,categorys)使用二分法或遍歷逐一尋找以name屬性值為sel的method_t(Method)届良,如果找到笆凌,以sel為鍵把method存入cache_t, 直接執(zhí)行mehtod里的IMP;
ALWAYS_INLINE static method_t *
search_method_list_inline(const method_list_t *mlist, SEL sel)
{
    int methodListIsFixedUp = mlist->isFixedUp();
    int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
    
    if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
        // 有序的話 進行二分查找(折半遍歷查找)
        return findMethodInSortedMethodList(sel, mlist);
    } else {
        // Linear search of unsorted method list
        // 無序則進行線性遍歷查找
        for (auto& meth : *mlist) {
            /// 對比方法名的地址
            if (meth.name == sel) return &meth;
        }
    }

#if DEBUG
    // sanity-check negative results
    if (mlist->isFixedUp()) {
        for (auto& meth : *mlist) {
            if (meth.name == sel) {
                _objc_fatal("linear search worked when binary search did not");
            }
        }
    }
#endif

    return nil;
}
  • findMethodInSortedMethodList,二分查找的具體實現(xiàn)
ALWAYS_INLINE static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
    ASSERT(list);

    const method_t * const first = &list->first;
    const method_t *base = first;
    const method_t *probe;
    uintptr_t keyValue = (uintptr_t)key;
    uint32_t count;
    
    for (count = list->count; count != 0; count >>= 1) {
        probe = base + (count >> 1);
        
        uintptr_t probeValue = (uintptr_t)probe->name;
        
        if (keyValue == probeValue) {
            // `probe` is a match.
            // Rewind looking for the *first* occurrence of this value.
            // This is required for correct category overrides.
            
            // 分類與類的同名方法,分類會放到最前面士葫,所以這里進行'--'操作后乞而,進行往前查找
            while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
                probe--;
            }
            return (method_t *)probe;
        }
        
        if (keyValue > probeValue) {
            base = probe + 1;
            count--;
        }
    }
    
    return nil;
}

方法緩存相關(guān)內(nèi)容請移步

6、消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā)流程
  • 動態(tài)方法解析resolveMethod_locked

/// 如果這個類不是元類則執(zhí)行__class_resolveInstanceMethod,如果是元類
/// 的話則執(zhí)行__class_resolveClassMethod,所以如果是類方法的話執(zhí)行resolveClassMethod,
/// 那么會一直查找最后還會執(zhí)行一次resolveinstancemethod,但是這個方法是
/// 執(zhí)行的元類的resolveinstancemethod而不是類的慢显,因為類是元類對象如果
/// 元類找不到就會往上層查找爪模,元類的上層是根元類,根元類的父類指向NSObject荚藻,
/// 所以最后還會執(zhí)行一次resolveInstanceMethod最終的實例方法屋灌。

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();

    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        if (!lookUpImpOrNil(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }

    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
1、嘗試調(diào)用自身的_class_resolveMethod動態(tài)為類對象或元類對象里添加方法實
現(xiàn)鞋喇。如果成功添加了method声滥,記錄已經(jīng)添加過
2、看是否在resolveClassMethod  或者 resolveInstanceMethod方法中為sel
提供了動態(tài)方法決議(可通class_addMethod方法添加)侦香,如果沒有提供則進入下
一步
3落塑、解析完成后繼續(xù)調(diào)用lookUpImpOrForward完成以下操作
if (fastpath(behavior & LOOKUP_CACHE)) {
        imp = cache_getImp(cls, sel);
        if (imp) goto done_nolock;
    }
  • 備用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector
  • 該方法只能讓我們把消息轉(zhuǎn)發(fā)到另一個能處理這個消息的對象,但是無法處理消息
    的內(nèi)容罐韩,比如參數(shù)和返回值

  • 該方法用于把消息轉(zhuǎn)發(fā)到另外一個可以處理該消息的對象憾赁,如果該aSelector
    另一個對象中實現(xiàn)了,則該對象就作為該消息新的接收值散吵,直接返回該對象龙考。

  • 完整消息轉(zhuǎn)發(fā)

運行時系統(tǒng)在這一步讓消息接收者將消息轉(zhuǎn)發(fā)給其他對象。當前對象會將對應(yīng)的sel -ector矾睦,目標(target)和參數(shù)打包成anInvocation對象轉(zhuǎn)發(fā)給其他對象

消息轉(zhuǎn)發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        if ([XXX instancesRespondToSelector:aSelector]) {
            signature = [XXX instanceMethodSignatureForSelector:aSelector];
        }
    }
    return signature;
}

forwardInvocation:方法的實現(xiàn)有兩個任務(wù):
(1)定位可以響應(yīng)封裝在anInvocation中的消息的對象晦款。這個對象不需要能處理所
有未知消息。
(2)使用anInvocation作為參數(shù)枚冗,將消息發(fā)送到選中的對象缓溅。anInvocation
會保留調(diào)用結(jié)果,運行時系統(tǒng)會提取這一結(jié)果并將其發(fā)送到消息的原始發(fā)送者

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = anInvocation.selector;
    XXX *x = [XXX new];
    if ([XXX instancesRespondToSelector:anInvocation.selector]) {
          [anInvocation invokeWithTarget:x];
    }
}
如果以上均未實現(xiàn)赁温,則無法識別selector坛怪,并拋出異常

7、runtime實踐:

1股囊、獲取對象的屬性袜匿、成員變量、方法和協(xié)議列表
2稚疹、通過消息轉(zhuǎn)發(fā)機制進行方法的攔截
3居灯、動態(tài)添加方法(class_addMethod
4、方法的動態(tài)交換(method-swizzling
5、將當前類與自定義類建立映射(Class object_setClass(id obj, Class cls)穆壕,由runtime的說明可知該函數(shù)的作用是為一個對象設(shè)置一個指定的類待牵,object內(nèi)部有一個叫做isa的變量指向它的class。這個變量可以被改變喇勋,而不需要重新創(chuàng)建缨该。然后就可以添加新的ivar和方法了〈ū常可以通過以下命令來修改一個object的class)
例: object_setClass([NSBundle mainBundle],[ModifyBundle class])贰拿,
NSBundle與ModifyBundle建立映射關(guān)系,將bundle的isa指針指向[ModifyBundle class]類熄云,在自定義Bundle類中重寫加載bundle
對應(yīng)的加載自建的XML類文件膨更、加載xib storyboard plist等文件、加載本地多
語言文件(.strings)等方法缴允,實現(xiàn)全局的多語言處理

8荚守、疑難解析

為什么要設(shè)計元類

  • 如果沒有元類,則類對象的信息和實例對象的信息都會存儲在類中练般,這樣對于查找對
    應(yīng)的信息矗漾,會更加復(fù)雜且混亂(如類方法和實例方法一樣的情況下,需要區(qū)分查找對
    象的類型)

  • 依次類推薄料,實例對象的方法列表在類對象的結(jié)構(gòu)體中敞贡,如果直接放在實例對象中,那
    么同一個類對象可能生成多個實例對象摄职,這些實例對象調(diào)用對應(yīng)的方法時誊役,內(nèi)存將會被
    爆掉的

  • 綜上,元類的設(shè)計符合單一原則和可擴展性谷市,實例對象管理自己的屬性信息蛔垢,實例對
    象的方法列表由類管理,類對象方法列表由元類管理迫悠,各司其職鹏漆,減少了對于對象類
    型和方法類型的判斷,增加了查找效率及皂;不同種類的方法走同一套流程甫男,大大節(jié)約了
    維護成本且改。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末验烧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子又跛,更是在濱河造成了極大的恐慌碍拆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異感混,居然都是意外死亡端幼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門弧满,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婆跑,“玉大人,你說我怎么就攤上這事。” “怎么了痪寻?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵娄昆,是天一觀的道長。 經(jīng)常有香客問我章贞,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任节槐,我火速辦了婚禮,結(jié)果婚禮上拐纱,老公的妹妹穿的比我還像新娘铜异。我一直安慰自己,他們只是感情好戳玫,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布熙掺。 她就那樣靜靜地躺著,像睡著了一般咕宿。 火紅的嫁衣襯著肌膚如雪币绩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天府阀,我揣著相機與錄音缆镣,去河邊找鬼。 笑死试浙,一個胖子當著我的面吹牛董瞻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播田巴,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼钠糊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了壹哺?” 一聲冷哼從身側(cè)響起抄伍,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎管宵,沒想到半個月后截珍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體攀甚,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年岗喉,在試婚紗的時候發(fā)現(xiàn)自己被綠了秋度。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡钱床,死狀恐怖荚斯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情查牌,我是刑警寧澤鲸拥,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站僧免,受9級特大地震影響刑赶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜懂衩,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一撞叨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浊洞,春花似錦牵敷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苫亦,卻和暖如春毛肋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屋剑。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工润匙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唉匾。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓孕讳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親巍膘。 傳聞我的和親對象是個殘疾皇子厂财,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

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

  • 方法 在類和對象[http://www.reibang.com/p/a0f6915c9116]篇中,我們了解到,...
    阿丶倫閱讀 365評論 0 0
  • OC的方法調(diào)用都是通過消息發(fā)送這種機制來實現(xiàn)的。當調(diào)用一個實例方法或者類方法時峡懈,底層實現(xiàn)是實例對象或者類對象調(diào)用o...
    YY_Lee閱讀 1,149評論 0 3
  • 轉(zhuǎn)載自:IOS開發(fā)工程師--周玉的博客:iOS開發(fā) 深入淺出Rumtime運行時之消息發(fā)送機制詳解 iOS開發(fā) R...
    路漫漫其修遠兮Wzt閱讀 499評論 0 3
  • 選擇器和SEL 類型 程序中的方法名(選擇器)在編譯后會被一個內(nèi)部標識符代替璃饱,內(nèi)部標識符所對應(yīng)的數(shù)據(jù)類型就是SEL...
    QiShare閱讀 541評論 0 1
  • 消息發(fā)送機制定義 OC的函數(shù)調(diào)用稱為消息發(fā)送。OC的消息發(fā)送屬于動態(tài)調(diào)用過程逮诲。即在編譯的時候并不能決定真正調(diào)用哪個...
    富有的心閱讀 253評論 1 1