Runtime(消息机制)

在使用runtime的时候,必须配置文件,buildSettings中搜索runtime,找到Enable Hardened Runtime选项,之后变成YES

  • runtime:必须要导入头文件 <objc/message.h>
  • 任何方法调用本质:发送一个消息,用runtime发送消息,OC底层实现通过runtime实现
  • 验证;方法调用,是否真的是转换为消息机制
  • runtime都有一个前缀,谁的事情使用谁
id objc = [NSObject alloc];
  • 类方法本质:类对象调用[NSObject class];
  • id;谁发送消息
  • SEL:发送什么消息
    ((NSObject ()(id, SEL))(void *)objec_msgSend)([NSObject class], @selector(alloc));
  • Xcode6之前,苹果运行使用objc_msgSend,而且有参数提示
  • Xcode6苹果不推荐我们使用runtime
解决方法提示
  • 找到build setting -> 搜索msg
  • 最终生成消息机制,编译器做的事情
  • 最终代码,需要把当前代码重新编译,用xcode编译器,clang
  • clang -rewrite-objc main.m 查看最终生成代码

runtime使用场景

  • 装逼
  • 不得不用,可以帮我们调用私有方法
Person *p = [Person alloc];
Person *p = objc_msgSend(obje_getClass("Person"), sel_registerName("alloc"));
p = [p init];
p = objc_msgSend(p,sel_registerName("init"));
runtime调用有参数的方法
objc_msgSend(p, @selector(run:), <参数>);
//可以传多个参数,看p对象中方法需要传的参数 
//@selector == sel_registerName

方法调用流程

面试回答

怎么区去调用eat方法,对象方法:类对象的方法列表 类方法:元类中方法类表

  • 1.通过isa区对应的类中查找
  • 2.注册方法编号
  • 3.根据方法编号去查找对应方法
  • 4.找到只是最终函数实现地址,根据地址去方法区调用对应函数

补充

5大区
  • 1.栈
  • 2.堆
  • 3.静态区
  • 4.常量区
  • 5.方法区
    栈:不需要手动管理内存, 自动管理
    堆:需要手动管理内存, 自己去释放

交换方法/自定义UIImage

  • 给系统的imageNamed添加功能,只能使用runtime(交换方法)
1.给UIImage添加一个分类
2.导入头文件
#import <objc/message.h>
3.先写一个方法,这个方法实现了实现了我们需要的功能(比如我们想让imageNamed方法中存在判断是否加载成功的功能,我们调用imageNamed方法的时候就可以实现)
+ (UIImage *)lht_iamgeNamed:(NSString *)name{
  UIImage *image = [UIImage lht_imageNamed:name];
  if(image){
    NSLog(@"加载成功");
  }else{
    NSLog(@"加载失败");
  }
}
4.在load中实现交换
+ (void)load{
//获取imageNamed
   //获取哪个类方法
   //SEL:获取哪个方法
Method imageNamedMethod = class_getClassMethod(self, @seletor(imageNamed:));
//获取lht_imageNamed
Method lht_imageNamedMethod = class_getClassMethod(self, @seletor(lht_imageNamed:));
//交换方法:runtime
 method_exchangeImplementations(imageNamedMethod, lht_imageNamedMethod)
}

动态添加方法(系统的类添加方法)

  • Runtime(动态添加方法):OC都是懒加载机制,只要一个方法实现了,就会马上添加到方法列表中
  • app:免费版,收费版
  • QQ,微博,直播等等应用,都有会员机制
美团面试题:有没有使用过performSelector?什么时候使用?动态添加方法的时候使用过。怎么动态添加方法?用runtime。为什么要用动态添加方法?
没有参数
调用
 Person *person = [[Person alloc] init];
 [person performSelector:@selector(eat)];

//void, (id, SEL)
//@相当于id, :相当于SEL, v相当于void

void aaa(id self, SEL _cmd){
    NSLog(@"吃");
}

//任何方法默认都有两个隐式参数,self, _cmd
//什么时候调用:只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理
//作用:动态添加方法,处理未实现

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selString = NSStringFromSelector(sel);
    if ([selString isEqualToString:@"eat"]) {
        //class:给哪个类添加方法
        //SEL:  添加哪个方法
        //IMP:  方法实现 => 函数 => 函数入口 => 函数名
        //type: 方法类型
        class_addMethod(self, sel, (IMP)aaa, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
带有参数
调用
 Person *person = [[Person alloc] init];
 [person performSelector:@selector(run:) withObject:@10];
void aaa(id self, SEL _cmd, NSNumber *meter){
    NSLog(@"跑了%@", meter );
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selString = NSStringFromSelector(sel);
    if ([selString isEqualToString:@"run:"]) {
        //class:给哪个类添加方法
        //SEL:  添加哪个方法
        //IMP:  方法实现 => 函数 => 函数入口 => 函数名
        //type: 方法类型
        class_addMethod(self, sel, (IMP)aaa, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

动态添加属性(系统的类添加属性)

  • 什么时候需要动态添加属性:给系统的类添加属性的时候,可以使用runtime动态添加属性方法
  • 本质:动态添加属性,就是让某个属性与对象产生关联
  • 需求:让NSObject类保存一个字符串
  • runtime一般都是准对系统的类

1.先给NSObject添加一个分类

2.在分类.h中定义属性

//property分类:只会生成get,set方法声明,不会生成实现,也不会生成下划线成员属性
@property NSString *name;

3.在分类.m中实现(实现get,set方法)

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

- (NSString *)name{

   return objc_getAssociatedObject(self, @"name");
}

4.外界调用

NSObject *objc = [[NSObject alloc] init];
objc.name = @"123";

自动生成属性代码(给系统的字典类新增加一个方法)

字典转模型的时候,我们需要在模型中按照字典的key一个一个的添加属性,很麻烦,我们就给字典添加一个分类,调用方法,直接生成属性,我们直接拷贝过去就好

1.给字典添加一个分类

2.在分类的.文件中声明方法

- (void) createPropertyCode;

3.在分类的.m文件中实现方法

- (void) createPropertyCode{
   NSMutableString *codes = [NSMutableString string];
 //遍历字典
   [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop){
   NSString *code;
   if([value isKindOfClass:[NSString class]]){
   
      code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@", key];
   
   }else if([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){
   
      code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@", key];
   
   }else if([value isKindOfClass:[NSNumber class]]){
   
      code = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@", key];
   
   }else if([value isKindOfClass:[NSArray class]]){
   
      code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@", key];
   
   }else if([value isKindOfClass:[NSDictionary class]]){
   
      code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@", key];
   
   }
   [codes appendFormat:@"\n%@\n", code];
   }];
   NSLog(@"%@", codes);
}

4.外界调用

//获取文件全路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"<文件全名称>" ofType:nil];
//文件全路径
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
//设计模型,创建属性代码 => dict
[dict createProperty];
  • 这个时候因为.m文件中我们打印出来了他根据字典的key转换出来的说有属性,我们直接复制,拷贝到模型类中,这样不仅快速,而且准确

字典转模型

1.给NSObject添加一个分类

2.在分类.h中方法声明

+ (instancetype)modelWithDict:(NSDictionary *)dict;

3.在分类.m中方法实现

  • Ivar:成员变量(带下划线的)
  • Property:属性
+ (instancetype)modelWithDict:(NSDictionary *)dict{
   id objc = [[self alloc] init];
   //runtime:根据模型中属性,去字典中取出对应的value给模型属性赋值
   /*
     第一个参数:获取哪个类的成员变量
     第二个参数:成员变量的个数
     */
   unsigned int count = 0;
    //获取成员变量数组
   Ivar ivarList = class_copyIvarList(self, &count);
    //遍历所有成员变量
    for (int i = 0; i < count; i++) {
        //获取成员变量
        Ivar ivar = ivarList[i];
        //获取成员变量类型
          NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
          //处理字符串(因为我们得到的字符串,他会带有一些没有用的东西,导致获取类失败,我们将这些都替换成空)
          ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
          ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
          
        //获取成员变量名字
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        //获取key
        NSString *key = [ivarName substringFromIndex:1];
        //去字典中查找对应value
        id value = dict[key];
  //二级转换
    if ([value isKindOfClass:[NSDictionary class]] && [ivarType hasPrefix:@"NS"]){
          //转换成哪个模型
          //获取类
          Class modelClass = NSClassFromString(ivarType);
          value = [modelClass modelWithDict:value];
    
    }
        //给模型中属性赋值
        if (value) {
            [objc setValue:value forkey:key];
        }
    }
    return objc;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iOS开发疯狂者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值