以forkingdog的PorotocolKit举例

举例

ProtocolKit

Protocol extension for Objective-C

Usage

Your protocol:

@protocol Forkable <NSObject>

@optional
- (void)fork; @required
- (NSString *)github; @end
Protocol extension, add default implementation, use @defs magic keyword @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
Your concrete class @interface Forkingdog : NSObject <Forkable>
@end @implementation Forkingdog - (NSString *)github {
return @"https://github.com/forkingdog";
} @end
Run test [[Forkingdog new] fork];
Result [Console] Forkable protocol extension: I'm forking (https://github.com/forkingdog).

  

实现

我们可以看到关键字是@def 查看其定义是

// 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

转化为oc语言定义就是

@protocol Forkable; \
@interface __PKContainer_Forkable_0 : NSObject <Forkable> @end \
@implementation __PKContainer_Forkable_0 \
@synthesize title = _title; + (void)load { \
//add protocol class method?
_pk_extension_load(@protocol(Forkable), __PKContainer_Forkable_0.class);
} -(NSString *)title
{
if (!_title) {
_title = @"default title!";
}
return _title;
} - (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

从这可以看出关键的代码 _pk_extension_load()

可以猜想其定义是

1 在load里注册自己包含那些扩展协议 
2 在c方法里遍历所有的类 把有扩展的 方法的实现指过去

现在查看其函数实现 关键代码如下

__attribute__((constructor)) static void _pk_extension_inject_entry(void) {

    pthread_mutex_lock(&protocolsLoadingLock);

    unsigned classCount = ;
Class *allClasses = objc_copyClassList(&classCount); @autoreleasepool {
for (unsigned protocolIndex = ; protocolIndex < extendedProtcolCount; ++protocolIndex) {
PKExtendedProtocol extendedProtcol = allExtendedProtocols[protocolIndex];
for (unsigned classIndex = ; 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 = , extendedProtcolCapacity = ;
}
//将协议的方法添加到到类中(如果不存在就不添加了)//包括类方法和实例方法
static void _pk_extension_inject_class(Class targetClass, PKExtendedProtocol extendedProtocol) { for (unsigned methodIndex = 0; methodIndex < extendedProtocol.instanceMethodCount; ++methodIndex) {
Method method = extendedProtocol.instanceMethods[methodIndex];
SEL selector = method_getName(method); if (class_getInstanceMethod(targetClass, selector)) {
continue;
} IMP imp = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
class_addMethod(targetClass, selector, imp, types);
} Class targetMetaClass = object_getClass(targetClass);
for (unsigned methodIndex = 0; methodIndex < extendedProtocol.classMethodCount; ++methodIndex) {
Method method = extendedProtocol.classMethods[methodIndex];
SEL selector = method_getName(method); if (selector == @selector(load) || selector == @selector(initialize)) {
continue;
}
if (class_getInstanceMethod(targetMetaClass, selector)) {
continue;
}
IMP imp = method_getImplementation(method);
const char *types = method_getTypeEncoding(method);
class_addMethod(targetMetaClass, selector, imp, types);
}
}

 由此可知猜想是正确的,方法告诉我们对于某一个含有该协议的类,如果其含有了协议里面的函数,则跳过,如果没有该函数就添加。

这里是下载地址

  

向OC类中添加默认的协议实现(ProtocolKit)的更多相关文章

  1. 在SpringMVC中,当Json序列化,反序列化失败的时候,会抛出HttpMessageNotReadableException异常, 当Bean validation失败的时候,会抛出MethodArgumentNotValidException异常,因此,只需要在ExceptionHandler类中添加处理对应异常的方法即可。

    在SpringMVC中,当Json序列化,反序列化失败的时候,会抛出HttpMessageNotReadableException异常, 当Bean validation失败的时候,会抛出Method ...

  2. AE 向已存在的要素类中添加字段

    风过无痕 原文向已存在的要素类中添加字段 以前,在用AE写程序的时候,为了方便,一般都是直接新建一个MapControl窗体应用程序.这次需要解决的问题用不到窗口,就突发奇想,直接新建了一个Conso ...

  3. C++中若类中没有默认构造函数,如何使用对象数组

    前言: 如果定义一个类,有其默认的构造函数,则使用new动态实例化一个对象数组,不是件难事,如下代码: #include <memory> #include <iostream> ...

  4. 类中添加log4j日志

    在编写代码的时候需要随时查看工作日志,查看工作日志的好处就是随时能检查出错误.所以我一般就需要在编写代码的前期添加工作日志,以便更好的查看相关错误输出. 以一个springmvc小demo为例子  主 ...

  5. 【2016.3.30项目技术记录】]VS2010自动生成MFC单文档框架程序的修改:去除属性框,在CViewTree类中添加鼠标单击响应

    转自http://blog.csdn.net/yanfeiouc2009/archive/2010/06/07/5653360.aspx 手头上有个东西要用到单文档,由于想省事,直接用VS2010做了 ...

  6. springboot在工具类中添加service的方法,显示为空的解决方案

    @Component// 1.将工具类声明为spring组件,这个必须不能忘 public class TestUtils { //2.自动注入 @Autowired private ItemServ ...

  7. C++空类中的默认函数

    定义一个空的C++类,例如 class Empty { } 一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,一般编译过去就相当于 cla ...

  8. 【c++】类中带默认参数的函数

    反思两个问题 1. 带默认参数的函数,为何声明.定义不能同时有参数? 2. 带默认参数的函数, 为何带默认参数的参数靠后站? 上程序 #include <iostream> #includ ...

  9. 给VS类文件添加默认头注释

    找到类文件所在路径:C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ItemTemplatesCache\CSharp\ ...

随机推荐

  1. 机房收费系统(VB.NET)——存储过程实战

    最初接触存储过程是在耿建玲老师的视频里,当初仅仅是草草过了一遍.仅仅是有了个印象.知道了这个名词:大二时也有SqlServer数据库这门课,只是老师没讲,自己也没看:真正对存储过程的了解来自于自学考试 ...

  2. Android源码是这样搞到的(图解)

    Android学习到一定程度,就一定要多读代码多思考,Android源码就是很好的学习材料,本文就是把Android的源码下载下来.我们知道Android的源码是用Git这个分布式版本号控制工具管理的 ...

  3. css 行内元素和块级元素

    1. 块级元素默认在新行开始,如常见的div和p标签,行内元素默认在同行开始显示,如a,span标签 2.块级元素一般用于做容器,可容纳行内和块级元素,可设置width和height,行内元素只能容纳 ...

  4. careercup-递归和动态规划 9.10

    9.10 给你一堆n个箱子,箱子宽w,高h,深d.箱子不能翻转,将箱子堆起来时,下面箱子的宽度.高度和深度必须大于上面的箱子.实现一个方法,搭出最高的一堆箱子,箱堆的高度为每个箱子高度的总和. 解法: ...

  5. js按值传递还是按引用传递?

    js和其他大部分语言一样,有基本类型和引用类型.因此访问变量就有按值和按引用两种方式,但是传参的时候却只能按值传递.基本类型作为参数时按值传递自然无可厚非,但引用类型作为参数也按值传递就让人有点困惑了 ...

  6. Java并发——Fork/Join框架

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4631466. ...

  7. 简单的实现QQ通信功能(一)

    第一部分:数据库的设计,数据集的建立 一:数据库的设计: 1.用户表:包含用户名.密码.昵称.性别.备注.状态.头像代号和最后登录时间. 2.朋友关系表:自增长列为主键列,用户名和好友名,还有朋友的状 ...

  8. WinForm界面(一)

    一:Form对象 属性: 设计中的Name:窗体类的类名AcceptButton:窗口的确定按钮CancelButton:窗口按ESC的取消按钮 1.外观 Backcolor:背景颜色Forecolo ...

  9. C# select的联动效果

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html><head&g ...

  10. 移动端rem布局的适配mixin【转藏】

    /*================================================================ 以下为基于ip5 宽度320做的适配,标准html{font-si ...