Runtime源碼 protocol(協(xié)議)

一、概述

協(xié)議定義了一個(gè)綱領(lǐng)性的接口,所有類都可以選擇實(shí)現(xiàn)。它主要是用來(lái)定義一套對(duì)象之間的通信規(guī)則。protocol也是我們?cè)O(shè)計(jì)時(shí)常用的一個(gè)東西,相對(duì)于直接繼承的方式,protocol則偏向于組合模式。他使得兩個(gè)毫不相關(guān)的類能夠相互通信,從而實(shí)現(xiàn)特定的目標(biāo)。因?yàn)镺C是單繼承的,由于不支持多繼承,所以很多時(shí)候都是用Protocol和Category來(lái)代替實(shí)現(xiàn)"多繼承"。

二、底層實(shí)現(xiàn)

在objc4-723中protocol的定義如下:

struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods;
    method_list_t *classMethods;
    method_list_t *optionalInstanceMethods;
    method_list_t *optionalClassMethods;
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;

    const char *demangledName();
    ...
};

我們可以看到protocol繼承自objc_object,里面的字段基本算是清晰,主要結(jié)構(gòu)如下:

  • mangledName 和 _demangledName
    這是來(lái)源于C++的name mangling(命名重整)技術(shù),在C++里面用來(lái)區(qū)別重載是的函數(shù)。

  • protocols
    它是protocol_list_t類型的指針,保存了這個(gè)協(xié)議所遵守的協(xié)議;

  • instanceMethods
    實(shí)例方法列表

  • calssMethods
    類方法列表

  • optionalInstanceMethods
    可選擇實(shí)現(xiàn)的實(shí)例方法,在聲明時(shí)用@optional關(guān)鍵字修飾的實(shí)例方法

  • optionalClassMethods
    可選擇實(shí)現(xiàn)的類方法,在聲明時(shí)用@optional關(guān)鍵字修飾的類方法

  • instanceProperties
    實(shí)例屬性

  • _classProperties
    類屬性,比較少見

    @property (nonatomic, strong) NSString *name;//通過(guò)實(shí)例調(diào)用
    @property (class, nonatomic, strong) NSString *className;//通過(guò)類名調(diào)用
    

三、 常用方法

  • protocol_copyPropertyList
    runtime提供了兩個(gè)方法:
objc_property_t *
protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
{
    return protocol_copyPropertyList2(proto, outCount, 
                                      YES/*required*/, YES/*instance*/);
}


objc_property_t *
protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount, 
                           BOOL isRequiredProperty, BOOL isInstanceProperty)
{
    if (!proto  ||  !isRequiredProperty) {
        // Optional properties are not currently supported.
        if (outCount) *outCount = 0;
        return nil;
    }

    rwlock_reader_t lock(runtimeLock);

    property_list_t *plist = isInstanceProperty
        ? newprotocol(proto)->instanceProperties
        : newprotocol(proto)->classProperties();
    return (objc_property_t *)copyPropertyList(plist, outCount);
}

// Optional properties are not currently supported.
這里指明了可選屬性現(xiàn)在還不支持,這就是沒有可選屬性的原因

  • conformsToProtocol()
+ (BOOL)conformsToProtocol:(Protocol *)protocol {
    if (!protocol) return NO;
    for (Class tcls = self; tcls; tcls = tcls->superclass) {
        if (class_conformsToProtocol(tcls, protocol)) return YES;
    }
    return NO;
}

- (BOOL)conformsToProtocol:(Protocol *)protocol {
    if (!protocol) return NO;
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (class_conformsToProtocol(tcls, protocol)) return YES;
    }
    return NO;
}

兩個(gè)方法都是遍歷類的繼承集體,調(diào)用class_conformsToProtocol方法,其實(shí)現(xiàn)如下:

BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen)
{
    protocol_t *proto = newprotocol(proto_gen);
    
    if (!cls) return NO;
    if (!proto_gen) return NO;

    rwlock_reader_t lock(runtimeLock);

    assert(cls->isRealized());

    for (const auto& proto_ref : cls->data()->protocols) {
        protocol_t *p = remapProtocol(proto_ref);
        if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) {
            return YES;
        }
    }

    return NO;
}

把class的protocols取出來(lái),并與傳入的protocol做比較,如果地址相同直接返回,或者協(xié)議"繼承"的層級(jí)中滿足條件:

/***********************************************************************
* protocol_conformsToProtocol_nolock
* Returns YES if self conforms to other.
* Locking: runtimeLock must be held by the caller.
**********************************************************************/
static bool 
protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other)
{
    runtimeLock.assertLocked();

    if (!self  ||  !other) {
        return NO;
    }

    // protocols need not be fixed up

    if (0 == strcmp(self->mangledName, other->mangledName)) {
        return YES;
    }

    if (self->protocols) {
        uintptr_t i;
        for (i = 0; i < self->protocols->count; i++) {
            protocol_t *proto = remapProtocol(self->protocols->list[i]);
            if (0 == strcmp(other->mangledName, proto->mangledName)) {
                return YES;
            }
            if (protocol_conformsToProtocol_nolock(proto, other)) {
                return YES;
            }
        }
    }

    return NO;
}

遞歸處理,對(duì)比協(xié)議的mangledName,有相同的就返回YES。

參考:
協(xié)議protocol
Protocol

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容