zoukankan      html  css  js  c++  java
  • 向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);
        }
    }
    

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

    这里是下载地址

      

  • 相关阅读:
    java程序员究竟应该掌握点什么
    Java程序员集合框架面试题
    数组名和数组名取地址的区别
    二维、三维数组转一维数组
    函数指针 行指针 指针数组
    转:如何成为一个优秀的程序员
    转:最小堆的数组实现
    for_each使用方法详解[转]
    c++虚函数的作用是什么?
    转:C语言 可变参数
  • 原文地址:https://www.cnblogs.com/nonato/p/4874246.html
Copyright © 2011-2022 走看看