文档章节

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
从C#到Objective-C,循序渐进学习苹果开发(3)--分类(category)和协议Protocal的理解

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

walb呀
2017/12/04
0
0
JavaScriptCore框架在iOS7中的对象交互和管理

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

北方人在上海
2016/03/28
30
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
总结objective-c特点

Objective-C与其它面向对象有这明显的不同,它有这自己鲜明的特色,下面我们从这个方法介绍它的特点:兼容性、字符串、类、方法、属性、协议和分类。 1. 兼容性 Objective-C可以说是一种面向...

晨曦之光
2012/05/16
239
0

没有更多内容

加载失败,请刷新页面

加载更多

redis-hash

哈希类型是指健值本身又是一个键值对结构 基本命令: hset key field value 设置值 hget(获取),hdel(删除),hlen(计算field个数),hmget(批量设置),hexists(是否存在),hkeys(获取所有的...

拐美人
14分钟前
1
0
简单的svm例子

数据来源:https://github.com/oumiga1314/Coursera-ML-AndrewNg-Notes/blob/master/code/ex6-SVM/data/ex6data1.mat import pandas as pd import numpy as np import scipy.io as sio impor......

南桥北木
18分钟前
0
0
android 关于View的一些整理

1、Button text的值为英文时,会自动转换成大写。如需取消,设置android:textAllCaps="false" 2、控件的可见性 可以在layout的配置文件中,配置android:visibility属性 调用setVisibility()...

西米小娅
28分钟前
0
0
Spring JDBC数据源分析

Spring数据源分析 分析这样一段代码: package com.jason.spring.datasource.jdbc;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframew......

宸明
36分钟前
1
0
FatJar:适用于sdk多module打包和合并多个jar的gradle插件

usage: 1.下载fatJar.gradle放置于project根目录 2.在project的build.gradle中添加依赖和配置: apply from: 'fatJar.gradle'buildscript { dependencies { classpath 'xyz......

SuShine
53分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部