runtime(四)-常见操作

2018-04-07  本文已影响4人  George_Luofz

runtime很复杂庞大,懂了oc对象布局,理解了发消息过程和消息转发流程,还不够。
接下来再学习一些常见用法,因为我们学到的那些毕竟是系统如何用的,自己会用才行。
runtime可以创建一个类,对一个类做各种改造:添加属性/变量、添加方法、添加协议

  1. 方法替换/新增方法
    替换两个方法的imp,一般将替换操作放在load方法中(load方法在类加载时就会触发,这样在程序运行时就已经替换完毕了,相当于一个速度的优化;而且该方法只会执行一次,不会出现重复替换的情况)
+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SEL originalSEL = @selector(say);
        SEL newSEL = @selector(newSay);
        Method originalMethod = class_getInstanceMethod(self, originalSEL);
        Method newMethod = class_getInstanceMethod(self, newSEL);
        // 添加一个方法
        BOOL flag = class_addMethod(self, originalSEL, method_getImplementation(newMethod), "v@:");
        if(flag){
            // 替换一个SEL的实现
            class_replaceMethod(self, newSEL, method_getImplementation(originalMethod), "v@:");
        }else{
            // 交换两个方法的实现
            method_exchangeImplementations(originalMethod, newMethod);
        }
    });
}
  1. 消息转发
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if([NSStringFromSelector(sel) isEqualToString:@"test"]){
        Method method = class_getInstanceMethod(self, @selector(_testIMP));
        bool success = class_addMethod(self, @selector(test), method_getImplementation(method), "v@:");
        if(success) return YES;
        return NO;
    }
    return NO;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if([NSStringFromSelector(aSelector) isEqualToString:@"test"]){
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    
    return [super methodSignatureForSelector:aSelector];
}
-(id)forwardingTargetForSelector:(SEL)aSelector{
//    if([NSStringFromSelector(aSelector) isEqualToString:@"test"]){
//        return [[TestObj alloc] init];
//    }
    return [super forwardingTargetForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    SEL sel = anInvocation.selector;
//    if([NSStringFromSelector(sel) isEqualToString:@"test"]){
//        TestObj *obj = [TestObj new];
//        if([obj respondsToSelector:sel]){
//            [anInvocation invokeWithTarget:obj];
//            return;
//        }
//    }
    [super forwardInvocation:anInvocation];
}

可以在三个位置处理消息转发,在runtime(二)有说明

  1. category加属性
static char * keyName = "KAPropKey";

- (void)setProp:(NSString *)prop{
    objc_setAssociatedObject(self, keyName, prop, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)prop{
   return objc_getAssociatedObject(self, keyName);
}

OBJC_ASSOCIATION_COPY_NONATOMIC有几种取值,参照doc:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

_NONATOMIC加下划线的表示非原子性,相当于@property(nonatomic)
关于category加属性如何实现,在category专题学习

  1. json转model
    其实思路就是利用runtime获取property列表,然后取出json中的键值对,使用KVC为每一个property赋值
    举个栗子:
- (void)_test_jsonToModel{
    NSDictionary *dict = @{@"prop":@"value",@"prop2":@"value2",@"prop3":@"value3"};
    
    TestRuntimeForward *model = [TestRuntimeForward new];

    unsigned int count = 0;
   // 获取属性列表
    objc_property_t *propList =  class_copyPropertyList(objc_getClass("TestRuntimeForward"), &count);
    for(int i = 0 ; i < count;i++){
        objc_property_t prop = propList[i];
        const char *name = property_getName(prop);
        NSString *nameStr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
        // 取值
        id value = [dict valueForKey:nameStr];
        if(value){
            // KVC设值
            [model setValue:value forKey:nameStr];
        }
    }
    free(propList);
    NSLog(@"model:%@",model);
}
输出结果
2018-04-07 12:36:28.983197+0800 iOSLearnigDemo[13232:1087932] model:prop=value

持续整理中

上一篇下一篇

猜你喜欢

热点阅读