Runtime

2020-05-26  本文已影响0人  Zorin

定义

runtime 详解文章


1. 消息转发

  1. 动态方法解析(方法调用的必经之路)
+ (BOOL)resolveClassMethod:(SEL)sel {
    // 获取方法名称
    NSString * selName = [NSString stringWithFormat:@"%@",NSStringFromSelector(sel)];
    // 当识别到 某个方法时 可以动态的添加进来
    if ([selName isEqualToString:@"testMethod"]) {
        //为该类动态添加一个方法testFunction处理消息,并执行该方法
        class_addMethod([self class], sel, (IMP)testFunction, "v@:");
        return true;
 }
    return false;

}
+ (BOOL)resolveInstanceMethod:(SEL)sel {

}
  1. 快速转发(动态解析失败后,指定一个接收方法的对象)
/// 快速转发
- (id) forwoardingTargetForSelector:(SEL)aSelector {
   NSString * methodName = NSStringFromSelector(aSelector);
  if ([methodName isEqualToString:@"sendMessage:"])
  {
     return [CustomClass new];
  }
  return  [super forwordingTargetForSelector:aSelector];
}
  1. 完整的消息转发(1. 消息签名 2. 转发请求)
#pragma mark 慢速转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSString * methodName = NSStringFromSelector(aSelector);
    if ([methodName isEqualToString:@"sendMessage:"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
};
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = [anInvocation selector];
    CustomClass * obj = [CustomClass new];
    if ([obj respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:obj];
    } else {
        [super forwardInvocation:anInvocation];
    }  
}

  1. 未识别到方法
- (void)doesNotRecognizeSelector:(SEL)aSelector {
      ///   未识别到方法的调用
}

2. Method Swizzling(方法转换)

+ (void)load {
    ///  对 原方法  和 新方法 进行调换
    Method originMethod  = class_getInstanceMethod(self,@selector(reloadData));
    Method currentMethod = class_getInstanceMethod(self,@selector(lg_reloadData));
    method_exchangeImplementations(originMethod,currentMethod);

}
/*
当调用 reload 的时候 就会自动调用 这个方法了
*/
- (void)lg_reloadData {
    [self lg_reloadData];
    [self fillDefaultView];
}
/*
当数据源没有数据的话,添加默认数据
*/
- (void)fillDefaultView {
    
}

3. 字典和模型的相互转换

/// 字典转模型
//1.  遍历字典的allKeys 
//2.  在for循环中,动态调用model 的 (key值前拼接set) 方法名,对model 的属性进行set赋值
- (instancetype)initWithDic:(NSDictionary *)dic {
    self = [super init];
    if (self) {
         for (NSString * key in dic.allKeys) {
               id value = dic[key];
               NSString * methodName = [NSString stringWithFormat:@"set%@:",key.capitalizedString];
               SEL sel = NSSelectorFromString(methodName);             
               if (sel) {
                     ((void(*)(id,SEL,id)) objc_msgSend) (self, sel, value);
               }

         }  
 
    }

}


/// 模型转字典
//1.  遍历模型的属性名称( class_getPropertyList(),获取属性列表 )
//2.  遍历过程中获取,属性value 值,赋值给字典
- (NSDictionary *)convertModelToDic {
    unsigned int count = 0;
    objc_property_t * properties = class_copyPropertyList(self,&count);
    if (count != 0) {
         NSMutalbeDictionary * tempDic = [@{ } mutableCopy];
         for (int i = 0; i < count; i ++ ) {
               //  属性名称
               const void * propertyName = property_getName(propertied[i]);
              // 根据属性名称 得到获取属性value 的get方法
               SEL sel = NSSelectorFromString(name);
               if (sel) { 
                     id value = ((id(*)(id, SEL, id )) objc_msgSend) (self, sel);
                    if (value) {
                            tempDic[name] = value;
                    } else {
                            temDic[name] = @"";
                    }      
               }
         }
         free(properties); //被copy到了堆区   要释放空间
         return tempDic;
    }
    free(properties);
    return  tempDic;
}

4. 自定义KVO

  1. 当一个对象的方法被监听时,会自动生成一个该对象父类的子类,
  2. 并且该对象指向这个新类,也就是多了一个中间类,这个中间类的作用就是重写,被监听属性的set方法,并发送被修改的通知
- (vod)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
      //1. 创建中间类
     Class customClass =  objc_allocateClassPair([self class],newName.UTF8String, 0);
     objc_registerClassPair(customClass);

     //  2. 修改self的isa指针,指向 中间类
     object_setClass(self, customClass);

     // 3. 重写 中间类的setter 方法 被通知 被通知者 值被改变
     NSString * methodName = [NSString stringWithFormat:@"set%@:",keyPath.capitalizedString];
     SEL sel = NSSelectorFromString(methodName);
     class_addMethod(customClass, sel, (IMP)setterMethod, "v@:@");
     objc_setAssociatedObject(self, (__bridge const void *)@"objc",observer,  OBJC_ASSOCIATION_ASSIGN);

}
void setterMethod(id self,SEL _cmd,NSString *name) {
      // 1、调用父类方法
      // 2、通知观察者
     struct objc_super superClass = {
            self,
            class_getSuperclass([self class])
     };
     //  调用父类的修改方法
     objc_msgSendSuper(&superClass, _cmd ,name);
    // 观察者
     id observer = objc_getAssociatedObject(self, (__bridge const  void *)@"objc");
    // 告诉观察者 数值被修改
   NSString * methodName = NSStringFromSelector(_cmd);
   NSString * key = @""; // keyPath 方法名 去除set 后 首字母小写
   objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),key,self,@{key:name},nil);
}





// 引用类型 和 值类型 ??/

上一篇下一篇

猜你喜欢

热点阅读