美文网首页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