收藏问题ios runtime专题iOS Developer

Runtime大体介绍(附代码)

2017-06-22  本文已影响85人  Corbin___

Runtime

一、简要介绍

// 这句代码是oc的,那么oc是怎么去实现这个代码的呢
Person *p = [Person allow];

// 这句就是上面那句oc代码在底层的实现
// 也就是说,你写了上面的那句代码后,其实oc的实现,就是调用了
// objc_msgSend方法,这个方法的作用是谁发送什么方法
// 整句的意思就是Person类发送allow的消息
Person *p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc"));

// 现在知道runtime是个什么东西了把,其实就是一套纯C语言写的API,给OC调用的,实现了OC的底层实现机制



objc_msgSend // 发送方法, 是对象的事情,所以是objc开头



// 这句就是Person类调用allow方法
// 底层的实现,就是Person发送了一个allow消息
Person *p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc"));


#import <objc/message.h>


- objc_msgsend()代码提醒没有参数,但是我们需要参数的

去项目,Build Setting msg 改为NO

二、应用

三、应用详细解释

1.为了装逼,仅仅装逼,oc的代码都可以换成runtime的API

  // oc创建
//    NSObject *objc = [[NSObject alloc] init];
//    
//    NSLog(@"%@",objc);
    
    // runtime创建
    
    // id:谁发送消息
    // SEL:发送什么消息
    // 以下代码就相当[NSObject alloc]
    id mySelf = objc_getClass("NSObject"); // 获取NSObject类
    SEL msgAlloc = sel_registerName("alloc"); // 发送alloc消息
    NSObject *objc = objc_msgSend(mySelf, msgAlloc); // NSObject类发送了alloc的消息
    
    objc = objc_msgSend(objc, sel_registerName("init")); // objc发送init消息
    
    NSLog(@"%@",objc);


2.利用runtime的消息机制,帮我们调用私有方法

开发觉得小伙伴有个类的私有方法写的很好,我想拿来用,所以这时候就只能通过runtime去拿

// 假如,我有一个类SendMsg,中有一个私有方法,正常的话oc是
调用不到私有方法的,所以我们用runtime来实现

 // 2.利用runtime的消息机制,帮我们调用私有方法
    
    SendMsg *sendMsg = [[SendMsg alloc] init];
    
    objc_msgSend(sendMsg, @selector(privateMethod));// 成功调用了私有方法
    
    // 回想下上面讲过的,oc方法的调用,就是发送一个消息
    // 那么上面那个代码就是发送一个privateMethod的消息



1.调用实例方法:是去类对象的方法列表中找这个实例方法名

2.调用类方法: 是去元类中的方法列表找

3.每个类对象都会有一个isa指针指向自身

1.通过isa指针去对应的类中查找

2.注册方法编号

3.根据方法编号去查找对应方法

4.找到只是最终函数实现的地址,根据地址去方法区调用实现的函数

image

3.交换方法-对系统的方法感到不满

1.要让[UIImage imageNamed:@"1.png"];添加成功输出成功

  // 图片
   UIImage *image = [UIImage imageNamed:@"1.png"];
    
    if (image) {
        NSLog(@"加载成功");
    } else {
        NSLog(@"加载失败");
    }


这样好麻烦,我每次都得去进行判断,这个重复的代码,程序猿不做重复的事情的

// 把上面的那个代码封装起来一个方法中,比如```myImageName```这个方法

   UIImage *image = [UIImage myImageName:name];
   
// 虽然这样ok了,看着挺成功,但是如果你的大佬跟你说,去维护一个项目,把这个项目的imageNamed都输出成功不成功,那么是不是都得改啊,改成myImageName调用,哎呦,还是很麻烦


// 尝试分类中重写系统方法
#import "UIImage+Exchange.h"

@implementation UIImage (Exchange)

+ (UIImage *)imageNamed:(NSString *)name {
    
    [self imageNamed:name]; // 死循环不行
    [super imageNamed:name]; // 父类是NSObject,没有这个方法
    
    // 总结:没法在分类中重写系统方法
    
}

1.runtime的API

分类代码,看下我的源码比较清晰一点


//
//  UIImage+Exchange.m
//  RuntimeDemo
//
//  Created by user on 2017/6/8.
//  Copyright © 2017年 陈泽槟. All rights reserved.
//

#import "UIImage+Exchange.h"
#import <objc/message.h>

@implementation UIImage (Exchange)

#pragma mark - 没有在分类中重写系统方法
//+ (UIImage *)imageNamed:(NSString *)name {
//    
//    [self imageNamed:name]; // 死循环不行
//    [super imageNamed:name]; // 父类是NSObject,没有这个方法
//    
//    // 总结:没法在分类中重写父类的方法
//    
//}
//

#pragma mark - 把类加载进内存的时候调用,只会调用一次
+ (void)load {
    
    // self : 获取哪个类的方法
    //@selector(imageNamed:) : 获取imageNamed方法的地址
    Method imageName =  class_getClassMethod(self, @selector(imageNamed:));
    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    
    // 交换两个方法的地址
    method_exchangeImplementations(imageName, imageWithName);
    
    
}


#pragma mark - 这个方法也是类加载进内存调用,但是会调用多次

+ (void)initialize {
    // 解决调用多次的方法
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
//        <#code#>
//    });
//    
}

#pragma  mark - 实现加载图片成功后的输出
+ (UIImage *)imageWithName:(NSString *)name {
    
    UIImage *image = [UIImage imageWithName:name];
    
    if (image) {
        NSLog(@"成功");
    }else {
        NSLog(@"失败");
    }
    return image;
    
}

@end

主类调用

    // 3.交换方法
    UIImage *image = [UIImage imageNamed:@"流程图.png"];
    
    // 这时候就会输出成功了

4、动态添加方法(看源码)

1.performSelector
2.resolveInstanceMethod

// 什么时候调用:只要一个对象调用了一个未使用的实例方法就会调用这个方法进行处理

// 作用:动态添加方法,处理未实现
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(eat)) {
        // 动态添加eat方法

        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod(self, @selector(eat), eat, "v@:");

    }

    return [super resolveInstanceMethod:sel];

}

每个方法都有隐式的两个参数

_cmd : 当前方法的编号

self :

5.动态添加属性

添加属性就是把这个属性跟这个类产生关联

  - (void)setName:(NSString *)name {
    // 第一个参数:给哪个对象添加属性
    // 第二个参数:属性名称
    // 第三个参数:属性值
    // 第四个参数:保存策略
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name {
    
    // 第一个参数:从哪里取
    // 第二个参数:取谁
    return objc_getAssociatedObject(self, @"name");
}
上一篇下一篇

猜你喜欢

热点阅读