runtime description

也是一套纯c语言的api。我们编写的OC代码,在程序运行过程中最终都转成了runtime的c语言代码。
a set of pure C Api. when executing, Objective C code finally convert to C runtime code. The core of runtime is Message Mechanism.

C & Objective C:

  • For C, function call will be determined in compile time
    • in compile time, call unimplemented function will be error
  • For Objective C, function call is dynamic. In the compile time, complier cannot determine which function will be called. Only when running, the function can be found and called by the specification name.
    • in compile time, can call any function if one declared, even if the function is not implemented.
    • in running time,

How runtime to work

Send message

let object sends message
objc_msgSend,only object can send message.
使用消息机制前提,必须导入#import

Simple example
clang -rewrite-objc main.m // see final code

    // 创建person对象
    Person *p = [[Person alloc] init];

    // 调用对象方法
    [p eat];

    // 本质:让对象发送消息
    objc_msgSend(p, @selector(eat));

    // 调用类方法的方式:两种
    // 第一种通过类名调用
    [Person eat];
    // 第二种通过类对象调用
    [[Person class] eat];

    // 用类名调用类方法,底层会自动把类名转换成类对象调用
    // 本质:让类对象发送消息
    objc_msgSend([Person class], @selector(eat));
  • Mechanism:
    • 对象根据方法编号SEL去映射表查找对应的方法实现
    • 类对象方法,去类对象方法列表找
    • 类方法去meta class方法列表找
    // TODO:讲解下什么时候runtime,
//    Person *p = [Person alloc];
    Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
//    p = [p init];

    p = objc_msgSend(p, sel_registerName("init"));

    // 调用eat
    [p eat];

    objc_msgSend(p, @selector(eat));
//    objc_msgSend(p, @selector(run:),20);
// QA:
// procedures of call a function
// 怎么去调用eat方法 ,对象方法:类对象的方法列表 类方法:元类中方法列表
// 1.通过isa去对应的类中查找
// 2.注册方法编号
// 3.根据方法编号去查找对应方法(注意不是方法名)
// 4.找到只是最终函数实现地址,根据地址去方法区调用对应函数

交换方法(methods swizzle)

Scenario:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。

  • method 1: 继承系统的类,重写方法.
  • method 2: 使用runtime,交换方法.

  • method 1: rewrite image

UIImage *image = [super imageNamed:image];
return image;
  • Cons:

    • 每次使用都要导入头文件 [FBImage imagenamed];

    • 必须要用自己定义的类名,如果项目太大,需要改的太多

  • method 2: method swizzling

@implementation ViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  // 需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。
  // 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;
  // 步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。
  UIImage *image = [UIImage imageNamed:@"123"];
}

@end

    @implementation UIImage (Image)
    // 加载分类到内存的时候调用
    + (void)load
    {
        // 交换方法

        // 获取imageWithName方法地址
        Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));

        // 获取imageWithName方法地址
        Method imageName = class_getClassMethod(self, @selector(imageNamed:));

        // 交换方法地址,相当于交换实现方式
        method_exchangeImplementations(imageWithName, imageName);


    }

        // 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.

    // 既能加载图片又能打印
    + (instancetype)imageWithName:(NSString *)name
    {

        // 这里调用imageWithName,相当于调用imageName
        UIImage *image = [self imageWithName:name];

        if (image == nil) {
            NSLog(@"加载空的图片");
        }

        return image;
    }
    @end

交换原理

交换之前:

交换之后:

动态添加方法

Scenario:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。

经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。

OC都是懒加载机制, 只要某个方法实现了就会马上添加到方法列表中。一些经常不用的方法,可以动态添加。比如VIP,收费功能。

简单使用

@implementation ViewController
(void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
Person *p = [[Person alloc] init];
// 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。
  // 动态添加方法就不会报错
  [p performSelector:@selector(eat)];
}
@end
@implementation Person
// void(*)()
// 默认方法都有两个隐式参数,
void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}
// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
(BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(eat)) {
      // 动态添加eat方法
  // 第一个参数:给哪个类添加方法
  // 第二个参数:添加方法的方法编号
  // 第三个参数:添加方法的函数实现(函数地址)
  // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
  if (sel = NSSelectorFromString(@"eat")
  class_addMethod(self, @selector(eat), eat, "v@:");
}
  return [super resolveInstanceMethod:sel];
}
@end

给分类添加属性

原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // 给系统NSObject类动态添加属性name

    NSObject *objc = [[NSObject alloc] init];
    objc.name = @"小码哥";
    NSLog(@"%@",objc.name);
}
@end


// 定义关联的key
static const char *key = "name";

@implementation NSObject (Property)

- (NSString *)name
{
    // 根据关联的key,获取关联的值。
    return objc_getAssociatedObject(self, key);
}

- (void)setName:(NSString *)name
{
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

results matching ""

    No results matching ""