向OC类中添加默认的协议实现(ProtocolKit)

以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 = 0;
    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;
}
View Code

 

//将协议的方法添加到到类中(如果不存在就不添加了)//包括类方法和实例方法
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);
    }
}

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

这里是下载地址

 

 

  

 

posted @ 2015-10-13 12:32  Nonato  阅读(1167)  评论(0编辑  收藏  举报