runtime

一、什么是RunTime?

RunTime简称运行时,OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的就是消息机制。
对于C语言来说,函数的调用在编译的时候回决定调用哪个函数。
对于OC来说,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
事实证明:
在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要生命过就不会报错
在编译阶段,C语言调用来实现的函数,就会报错

二、RunTime作用

1.发送消息

任何方法调用的本质,就是发送消息
objc_msgSend,只有对象才能发送消息,因此以objc开头
使用消息机制前提,必须导入#import <objc/message.h>
RunTime都有一个前缀,说的事情使用谁

[[NSObject alloc]init];
//最终生成消息机制,编译器做的事情
//最终代码:需要把当前代码重新编译,用Xcode编译器,clang
//clang -rewrite-objc main.m  查看最终生成代码
//[NSObject alloc]转化为消息
//id 谁发送消息
//SEL:  发送什么消息
id objc = ((NSObject *(*)(id, SEL))(void *)objc_mesSend)([NSObject class], @selector(alloc)); 
//开发中
//找到build setting-->搜索msg
id objc = objc_msgSend([NSObject class], @selector(alloc));
等价于:
id objc = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));
//[objc init]转消息

objc = objc_msgSend(objc, @selector(init));

可以帮我们调用私有的方法,OC中没有真正私有的东西,因为有消息机制

调用带参数的方法

objc_msgSend(objc, @selector(run:), 20);   //方法后面是需要传的参数

方法调用的流程:

怎么去调用一个方法?

如果是调用一个对象方法的时候:对象方法保存到类对象中,通过指针找到对应的对象,然后通过对象的isa指针找到对应的类对象,然后就可以调用类对象中的方法了。

如果是调用一个类方法:类方法保存在元类对象中,通过类对象的isa指针可以找到元类对象,然后在元类对象中可以找到方法对应的方法编号。

2.交换方法

需求:每次让UIImage加载图片的时候,告诉我是否加载成功

1.自定义UIImage
弊端:每次使用,都需要导入
项目大了,没办法实现
2.runtime
给系统的imageNamed添加工能,只能使用runTime
-给系统的方法添加分类
-自己实现一个带有扩展功能的方法
-交换方法,只需要交换一次

#import "UIImage+Image.h"
#import <objc/message>
@implementation UIImage (Image)
+(void)load
{
    //把类加载进内存的时候调用,只会调用一次
    //获取类方法
    Method imageNamedMethod = class_getClassMethod(self,@selector(imageNamed:));
     Method imageNamedMethod = class_getClassMethod(self,@selector(wly_imageNamed:));
    method_exchangeImplementations();
}

+(void)initialize
{
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
      
   });
}

+(UIImage *)wly_imageNamed:(NSString *)name
{
    UIImage *image = [UIImage wly_imageNamed:name];   //注意这里也要改,否则会造成死循环
    if(image){
       NSLog(@"加载成功");
    }else{
       NSLog(@"加载失败");
    }
    return image;
}

@end

3.动态添加方法

RunTime:OC都是懒加载机制,只要一个方法实现了,它就会马上添加到方法列表中
适用于一些不常用的功能

@implementation Person

///任何方法都有两个隐式参数self,_cmd
//_cmd:当前方法的编号
+(BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"NSStringFromSelector(sel)");
    //if([NSStringFromSelector(sel) isEqualToString:@"eat"])
    if(sel == NSSelectorFromString(@"eat")){
       //给哪个类添加方法
       //SEL:添加哪个方法
       //IMP:方法的实现==函数名
       //type:方法的类型
       class_addMethod(self, sel, (IMP)method, "v@:");  //查文档
    }else{
    
    }
    //动态添加方法,处理未实现的方法
    return [super resolveInstanceMethod:sel];
}
//函数,没有返回值

void method(id self, SEL _cmd){


}
@end

4.动态添加属性

需求:让一个NSObject类保存一个字符串

NSObject *objc = [[NSObject alloc] init];

//添加一个分类
@interface NSObject (Property)
//只会生成get,set方法的声明,不会生成实现,也不会生成下划线成员属性
@property NSString *name;
@end

@implementation NSObject (Property)
static NSString *_name;
-(void)setName:(NSString *)name
{
    //让这个字符串与当前对象产生联系
    _name = name;
}

-(NSString *)name
{
   return _name;
}

@end

使用runtime

//添加一个分类
@interface NSObject (Property)
//只会生成get,set方法的声明,不会生成实现,也不会生成下划线成员属性
@property NSString *name;
@end

@implementation NSObject (Property)
//static NSString *_name;
-(void)setName:(NSString *)name
{
    //让这个字符串与当前对象产生联系
    //object:给哪个对象添加属性
    //key:属性名称
    //value:属性值
    //policy:保存策略
    objc_setAssociatedObject(self,@"name",name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}

-(NSString *)name
{
   return  objc_getAssociatedObject(self,@"name");
}

@end

5.字典转模型


NSString *filePath = [NSBundle mainBundle] pathFoResource:@"123.plist" ofType:nil];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
WLyDataModel *data = [WLyDataModel itemWithDict:dict];


//根据dictionary中的key自动生成属性代码
//调用[dict createProperty];

-(void)createProperty
{   NSMutableString *codes = [NSMutableString string];
    [self enumerateKeysAndObjectsUsingBlock:^(id _Nullable key, id _Nullable value, BOOL _Nullable stop){
         if([value isKindOfClass:[NSString class]]){
               //判断是否是当前类或者是子类
               NSString *code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@", key];
         
         }else if(){
               //基本数据类型注意修改修饰符
         }
         [codes appendString:code];
         [codes appendString:@"\n"];
        
    }];
    NSLog(@"%@", codes);
}

利用KVC字典转模型

[item setValuesForKeysWithDictionary:dict];  //把字典中所有值给模型的属性赋值
[dict enumerateKeyAndObjectUsingBlock:^(id _Nullable key, id _Nullable value, BOOL * _Nullable){
    //要去模型中查找有没有对应的key
    [item setValue:value forKey:key];  
   //去模型中查找有没有对应的属性的set方法,有的话就把value传给set方法,调用赋值
   //找不到的话,去模型中查找有没有对应的属性,有的话直接访问属性赋值
   //去模型中查找有没有_source属性,有的话直接访问属性赋值
   //找不到,就会直接报错
}];

但是往往模型只保存最重要的是数据,模型中的数据和字典中的数据并不是一一对应的,因此,这种情况下,我们就不能使用KVC了

如果要处理找不到Key的问题,可以重写setValue:forUndefinedKey:(NSString *)key

//系统找不到就会调用这个方法报错
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    
}

利用RunTime字典转模型

//NSObject + model.h
#import <objc/message.h>
@implementation NSobject (Model)
+(instancetype)modelWithDict:(NSDictionary *)dict
{
	id objc = [[self alloc] init];
	//runTime:根据模型中属性,去字典中取出对应的value给模型赋值
	//获取模型中所有属性
	//根据属性名去字典中查找value
	//给模型中属性赋值  KVC
	//class_copyPropertyList();  获取属性
	/*
     获取那个类的成员变量
     成员变量的总数
     */
     int count = 0;
	Ivar *ivarList = class_copyIvarList(self, &count);
	for(int i = 0; i < count; i++){
       Ivar ivar = ivarList[i];
	   NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];  //拿到成员变量的名字
	   id value = dict[ivarName];
	    //把字典转换成模型
	   NSString *str = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
	    //获取类
	   [str stringByReplaceingOccurrencesOfString:@"\" withString:@""];
	   [str stringByReplaceingOccurrencesOfString:@"@" withString:@""];
	   if(value isKindOfClass:[NSDictionary class] && [str hasPrefix:@"NS"]){
	       
	        Class modelClass = NSClassFromString(str);
	        value = [modelClass modelWithDict:value];
          
       }
       if(value){
           [objc setValue:dict[ivarName] forKey:ivarName];
       }  
    }
    return objc;
}
@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值