objective-c 中实现协议扩展 ProtocolKit 源码分析
objective-c 中实现协议扩展 ProtocolKit 源码分析
大少爷t 发表于11个月前
objective-c 中实现协议扩展 ProtocolKit 源码分析
  • 发表于 11个月前
  • 阅读 9
  • 收藏 0
  • 点赞 0
  • 评论 0

标题:腾讯云 新注册用户域名抢购1元起>>>   

摘要: ProtocolKit 源码分析
ProtocolKit

可以这样子使用:


@protocol Forkable <NSObject>

@optional
- (void)fork;

@required
- (NSString *)github;

@end

// Protocol Extension

@defs(Forkable)

- (void)fork {
    NSLog(@"Forkable protocol extension: I'm forking (%@).", self.github);
}

- (NSString *)github {
    return @"This is a required method, concrete class must override me.";
}

@end

// Concrete Class

@interface Forkingdog : NSObject <Forkable>
@end

@implementation Forkingdog

- (NSString *)github {
    return @"https://github.com/forkingdog";
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [[Forkingdog new] fork];
    }
    return 0;
}

可以看出,在实现Forkable协议的具体类中不用实现fork 方法,就能在实例中使用fork方法。

用法,定义完协议之后,给协议添加默认的方法,

@defs(Forkable)

使用def关键字,Xcode 可以渲染成红色。保留关键字。

此处是一大堆关键的宏。宏,宏,不用怕,我们来展开一下。

// For a magic reserved keyword color, use @defs(your_protocol_name)
#define defs _pk_extension

// Interface
#define _pk_extension($protocol) _pk_extension_imp($protocol, _pk_get_container_class($protocol))

// Implementation
#define _pk_extension_imp($protocol, $container_class) \
    protocol $protocol; \
    @interface $container_class : NSObject <$protocol> @end \
    @implementation $container_class \
    + (void)load { \
        _pk_extension_load(@protocol($protocol), $container_class.class); \
    } \

// Get container class name by counter
#define _pk_get_container_class($protocol) _pk_get_container_class_imp($protocol, __COUNTER__)
#define _pk_get_container_class_imp($protocol, $counter) _pk_get_container_class_imp_concat(__PKContainer_, $protocol, $counter)
#define _pk_get_container_class_imp_concat($a, $b, $c) $a ## $b ## _ ## $c

void _pk_extension_load(Protocol *protocol, Class containerClass);

展开后如下:

@protocol Forkable; 
@interface __PKContainer_Forkable_0 : NSObject <Forkable>
@end
@implementation __PKContainer_Forkable_0 
+ (void)load { 
_pk_extension_load(@protocol(Forkable), __PKContainer_Forkable_0.class);
 }

- (void)fork {
    NSLog(@"Forkable protocol extension: I'm forking (%@).", self.github);
}

- (NSString *)github {
    return @"This is a required method, concrete class must override me.";
}

@end

新生成了一个类,并且新生成的类实现了Forkable 协议。然后在 load方法中执行_pk_extension_load

我们已经走完了宏干的事情。接来下看看函数做的事情,主要有两个函数:

_pk_extension_load  //在load函数里把实现的协议方法签名放在全局的数组中
_pk_extension_inject_entry // 遍历所有class实现了对应的协议,并且没有实现方法,就把方法动态注入

调用过程,如下图所示。

 

技巧:使用__attribute__((constructor)) 可以在mian函数调用之前做事情

_pk_extension_inject_entry 就是利用 __attribute__ 在main函数开始之前,实现class本身没有实现协议方法的注入。

__attribute__((constructor)) static void _pk_extension_inject_entry(void) {
    //上锁,保证多线程安全
    pthread_mutex_lock(&protocolsLoadingLock);

    unsigned classCount = 0;
    //获取所有的class
    Class *allClasses = objc_copyClassList(&classCount);
    
    @autoreleasepool {
        //遍历全局保存的方法签名
        for (unsigned protocolIndex = 0; protocolIndex < extendedProtcolCount; ++protocolIndex) {
            PKExtendedProtocol extendedProtcol = allExtendedProtocols[protocolIndex];
            for (unsigned classIndex = 0; classIndex < classCount; ++classIndex) {
                Class class = allClasses[classIndex];
                if (!class_conformsToProtocol(class, extendedProtcol.protocol)) {
                    continue;
                }
                //注入方法
                _pk_extension_inject_class(class, extendedProtcol);
            }
        }
    }
    pthread_mutex_unlock(&protocolsLoadingLock);
    
    free(allClasses);
    free(allExtendedProtocols);
    extendedProtcolCount = 0, extendedProtcolCapacity = 0;
}

 

共有 人打赏支持
粉丝 0
博文 2
码字总数 3042
×
大少爷t
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: