文档章节

objective-c 中实现协议扩展 ProtocolKit 源码分析

 大少爷t
发布于 2017/02/10 10:31
字数 566
阅读 15
收藏 0
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
作品 0
私信 提问
JavaScriptCore框架在iOS7中的对象交互和管理

之前一篇的文章中已经简单入门了iOS7中新加的JavaScriptCore框架的基本用法,十分的简单方便而且高效,不过也仅限于数值型、布尔型、字符串、数组等这些基础类型。本文将扩展到更复杂的类型,...

北方人在上海
2016/03/28
30
0
从C#到Objective-C,循序渐进学习苹果开发(3)--分类(category)和协议Protocal的理解

本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验。本文继续上一篇随笔...

walb呀
2017/12/04
0
0
FreeBSD 10 将使用 Clang 编译器替换 GCC

来自 phoronix 的消息称,根据 FreeBSD 2012 第一季度的状态报告 显示,来自 LLVM 的 Clang 编译器将成为 FreeBSD 10 的默认 C/C++ 编译器,废弃使用 GPL 授权协议的 GCC,而 Clang 的授权协...

oschina
2012/05/13
4.8K
16
React Native 从入门到原理

React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几。 本文分为两个部分:上半部分用通俗的语...

guozhendan
06/26
0
0
Runtime of Objective-C

- (void)forwardInvocation:(NSInvocation *)anInvocation { if ([someOtherObject respondsToSelector:[anInvocation selector]]) [anInvocation invokeWithTarget:someOtherObject]; else ......

Im刘亚芳
2014/12/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

给女朋友讲解什么是Optional【JDK 8特性】

前言 只有光头才能变强 前两天带女朋友去图书馆了,随手就给她来了一本《与孩子一起学编程》的书,于是今天就给女朋友讲解一下什么是Optional类。 至于她能不能看懂,那肯定是看不懂的。(学到...

Java3y
8分钟前
0
0
2019年六大新兴信息安全方向

导读 黑客攻击和网络犯罪的威胁正在不断升级,相应的技术“军备竞赛”正愈演愈烈,对于信息安全从业人员来说,掌握最新的信息安全工具,是在信息安全战争中生存下来的关键所在。 从特朗普的手...

问题终结者
15分钟前
0
0
redis扩展-自定义PropertyPlaceholderConfigurer,在spring属性注入之前,手动将properteis合并到spring容器中

背景:spring容器启动过程中,通过PropertyPlaceholderConfigurer读取properties配置文件,并将properties配置文件中的值注入spring bean的属性中, PropertyPlaceholderConfigurer使用方式多...

燃犀
17分钟前
0
0
PostgreSQL SPI 中的错误处理

PostgreSQL SPI 用于在 C 或是其他编程语言编写的扩展函数(存储过程)中调用数据库本身的解析器、规划器和执行器的功能,以及对 SQL 语句进行执行。 在最重要的一个函数 SPI_execute 的文档...

helloclia
18分钟前
0
0
深入理解Java内存模型

1 内存模型产生背景 在介绍Java内存模型之前,我们先了解一下物理计算机中的并发问题,理解这些问题可以搞清楚内存模型产生的背景。物理机遇到的并发问题与虚拟机中的情况有不少相似之处,物...

小刀爱编程
32分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部