美文网首页iOS开发笔记
objc_runtime给类目添加属性关联(objc_setAs

objc_runtime给类目添加属性关联(objc_setAs

作者: 祥子_HelloWorld | 来源:发表于2019-08-03 00:56 被阅读0次

    在上一篇我们了解了什么是Runtime的关联,接下来我们就给类目添加属性。

    1. 给NSObject类动态添加属性

    头文件定义部分

    @interface UIWebView (LoadProgress)  
      
    @property (nonatomic, assign) NSInteger resourceCount;  
    
    @end  
    

    实现部分
    首先要定义一个全局的key

    // resourceCount object keys  
    static void *s_resourceCountKey = &s_resourceCountKey;  
    //static void *s_resourceCountKey = "s_resourceCountKey";  
    
    @implementation UIWebView (LoadProgress)  
    @dynamic resourceCount;  
    
    #pragma mark Accessors and mutators  
      
    - (NSInteger)resourceCount  
    {  
        NSNumber *resourceCountNumber = objc_getAssociatedObject(self, s_resourceCountKey);  
        if (! resourceCountNumber)  
        {  
            return 0;  
        }  
        else  
        {  
            return [resourceCountNumber integerValue];  
        }  
    }  
      
    - (void)setResourceCount:(NSInteger)rCount  
    {  
        objc_setAssociatedObject(self, s_resourceCountKey, [NSNumber numberWithInteger:rCount], OBJC_ASSOCIATION_RETAIN_NONATOMIC);  
    }  
    
    ...
    

    这样就可以直接使用了
    webView.resourceCount = 10;

    问:使用runtime Associatedealloc方法关联的对象,需要在对象deadlloc的时候释放吗?

    答:不管是在MRC还是在ARC下,均不需要手动去释放。根据WWDC 2011, Session 322 中发布的内存销毁时间表,被关联的对象在生命周期内要比对象本身释放的晚很多。它们会在NSObject的deadlloc方法中调用object_dispose( )方法中释放。

    //对象的内存销毁时间表
    1. 调用-release :引用计数变为0
      * 对象正在被销毁,生命周期即将结束
      * 不能再有新的 __weak 弱引用,否则将指向nil
      * 调用 [self dealloc]
    
    2. -dealloc
      * 继承关系中最底层的子类再调用-dealloc
      * 如果是MRC代码,则会手动释放实例变量 iVars
      * 继承关系中每一层的父类都在调用-dealloc
    
    3. NSObject 调用 -dealloc
      * 只做一件事:调用Objective-C runtime 中的 object_dispose() 方法
    
    4. 调用object_dispose()
      * 为C++ 的实例变量们iVars 调用iVarsdestructors
      * 为ARC状态下的实例变量们 iVars调用-release
      * 解除所有使用runtime Associate方法关联的对象
      * 解除所有__weak 引用
      * 调用free()
    
    2. 替换或变更NSObject类方法Method

    Runtime不仅可以关联属性,还可以替换已有方法、添加新的方法等,基本替换方法:

    // 替换类方法  
    // frome: CoconutKit  
    IMP HLSSwizzleClassSelector(Class clazz, SEL selector, IMP newImplementation)  
    {  
        // Get the original implementation we are replacing  
        Class metaClass = objc_getMetaClass(class_getName(clazz));  
        Method method = class_getClassMethod(metaClass, selector);  
        IMP origImp = method_getImplementation(method);  
        if (! origImp) {  
            return NULL;  
        }  
          
        class_replaceMethod(metaClass, selector, newImplementation, method_getTypeEncoding(method));  
        return origImp;  
    }  
      
    // 替换实例方法  
    IMP HLSSwizzleSelector(Class clazz, SEL selector, IMP newImplementation)  
    {  
        // Get the original implementation we are replacing  
        Method method = class_getInstanceMethod(clazz, selector);  
        IMP origImp = method_getImplementation(method);  
        if (! origImp) {  
            return NULL;  
        }  
          
        class_replaceMethod(clazz, selector, newImplementation, method_getTypeEncoding(method));  
        return origImp;  
    }  
    

    新方法定义

    // Original implementation of the methods we swizzle  
    static id (*s_UIWebView__identifierForInitialRequest_Imp)(id, SEL, id, id, id) = NULL;  
      
    // Swizzled method implementations  
    static id swizzled_UIWebView__identifierForInitialRequest_Imp(UIWebView *self, SEL _cmd, id webView, id initialRequest, id dataSource);  
    

    方法初始化需要写在类方法+ (void)load中

    + (void)load  
    {  
        s_UIWebView__identifierForInitialRequest_Imp = (id (*)(id, SEL, id, id, id))HLSSwizzleSelector(self, @selector(webView:identifierForInitialRequest:fromDataSource:), (IMP)swizzled_UIWebView__identifierForInitialRequest_Imp);  
    }  
    

    实现部分

    #pragma mark Swizzled method implementations  
      
    static id swizzled_UIWebView__identifierForInitialRequest_Imp(UIWebView *self, SEL _cmd, id webView, id initialRequest, id dataSource)  
    {  
        // 调用原方法  
        (*s_UIWebView__identifierForInitialRequest_Imp)(self, _cmd, webView, initialRequest, dataSource);  
          
        [self setResourceCount:self.resourceCount+1];  
          
        return [NSNumber numberWithInteger:self.resourceCount];  
    }  
    
    3. 代理方法检索

    可直接定义到NSObject的Category中

    // [self implementsProtocol:@protocol(UIActionSheetDelegate)]  
    
    // frome: CoconutKit  
    - (BOOL)implementsProtocol:(Protocol *)protocol  
    {  
        // Only interested in optional methods. Required methods are checked at compilation time  
        unsigned int numberOfMethods = 0;  
        struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, NO /* optional only */, YES, &numberOfMethods);  
        for (unsigned int i = 0; i < numberOfMethods; ++i) {  
            struct objc_method_description methodDescription = methodDescriptions[i];  
            SEL selector = methodDescription.name;  
            if (! class_getInstanceMethod([self class], selector)) {  
                NSString *selectorString = [NSString stringWithCString:sel_getName(selector) encoding:NSUTF8StringEncoding];  
                NSString *protocolName = [NSString stringWithCString:protocol_getName(protocol) encoding:NSUTF8StringEncoding];  
                HLSLoggerInfo(@"Class %@ does not implement method %@ of protocol %@", [self className], selectorString, protocolName);  
                selectorString = nil;               // Just to remove unused variable warnings  
                protocolName = nil;  
                return NO;  
            }  
        }  
          
        return YES;  
    }  
    

    相关文章

      网友评论

        本文标题:objc_runtime给类目添加属性关联(objc_setAs

        本文链接:https://www.haomeiwen.com/subject/lfnldctx.html