iOS开发iOS面试相关

iOS面试-Runtime简介

2018-10-07  本文已影响280人  翀鹰精灵

本文主要介绍runtime的五点

一.runtime简介

runtime的五个功能

1.发送消息
2.交换方法
3.动态添加方法
4.给分类添加属性
5.字典转模型

二.Runtime(消息机制)

消息机制是OC中是最主要的机制,任何方法的调用本质都是发送消息,用runtime发送消息,OC底层通过runtime来实现。
验证:
是否真的是转换为消息机制
第一步:cd进入到该文件夹
第二步:在终端执行clang -rewrite-objc 文件名 命令
第三步:该命令成功后就会生成一个.cpp的文件。

allisondeMacBook-Pro:123 allison$ clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m
clang: warning: using sysroot for 'iPhoneSimulator' but targeting 'MacOSX' [-Wincompatible-sysroot]
/var/folders/nm/4974_cj15s5gyjfg8wj077qm0000gn/T/ViewController-78b550.mi:12798:188: warning: 
      'kCFCalendarUnitWeek' is deprecated: first deprecated in macOS 10.10 - Use
      kCFCalendarUnitWeekOfYear or kCFCalendarUnitWeekOfMonth instead
      [-Wdeprecated-declarations]
  ...or NSCalendarUnitWeekOfYear, depending on which you mean"))) = kCFCalend...
                                                                    ^
/var/folders/nm/4974_cj15s5gyjfg8wj077qm0000gn/T/ViewController-78b550.mi:5147:2: note: 
      'kCFCalendarUnitWeek' has been explicitly marked deprecated here
 kCFCalendarUnitWeek __attribute__((availability(macos,introduced=10.4,d...
 ^
1 warning generated.

查看ViewController.cpp文件,我们发现有64871行代码,查找viewDidLoad方法,我们看.cpp文件中viewDdiload方法中生成的代码:

// @implementation ViewController

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));


    id objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));
    objc = ((id (*)(id, SEL))(void *)objc_msgSend)((id)objc, sel_registerName("init"));

}

我们ViewController.m写的代码

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    id objc = [NSObject alloc];
    objc = [objc init];
}

例子:运用运行时去创建一个NSObject对象。(我们可以参考ViewController.cpp类里面的viewDidLoad方法)

    id objc = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));
    objc = ((id (*)(id, SEL))(void *)objc_msgSend)((id)objc, sel_registerName("init"));

上面这个是最底层的,下面我们可以用苹果封装好的方法,效果是一样的:

    id objc = objc_msgSend([NSObject class], @selector(alloc));
    objc = objc_msgSend(objc, @selector(init));

这里,从控制台我们可以发现,我们已经分配内存,并且没有错误。


msg01.png

Tips1

本来想在main函数中试验,但是在main函数中的时候,使用 allison$ clang -rewrite-objc main.m命令的时候总是报如下错误,所以最后在ViewController中生成cpp文件成功。(暂不清楚原因,还请各位知道的留言指正)

allisondeMacBook-Pro:Clang allison$ clang -rewrite-objc main.m
main.m:9:9: fatal error: 'UIKit/UIKit.h' file not found
#import <UIKit/UIKit.h>
        ^~~~~~~~~~~~~~~
1 error generated.
allisondeMacBook-Pro:

Tips2

我们用objc_msgSend函数的时候,可能没有提示,这是因为在xcode6之后,苹果不推荐使用runtime,因为苹果不想让开发者了解底层的实现,只想让开发者直接使用API即可。但是这里,我们可以将严格检测消息机制这个选项关闭,使用objc_msgSend函数的时候便会提示了,设置如下图所示:

msg02.png msg03.png
三. Runtime(消息机制调用多个参数)

开发中什么时候用runtime?

1.可以帮我们调用私有的方法。(例子:同事封装的一个比较好的类,该类中都是私有方法,我们想使用该方法,一般是不会直接修改.h文件,这个时候可以使用runtime来实现)。

2.苹果系统API中,如果发现某个私有的API比较好用,如果我们想使用,可以通过objc_msgSend来调用私有的api。

四.方法调用流程

调用set方法对象方法:类对象的方法列表;类方法:元类中方法列表。

方法调用流程:

方法调用流程.png

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

五.Runtime(交换方法&自定义UIImage)

开发中什么时候使用runtime(交换方法)?
需求1:每次UIImage加载图片,返回是否加载成功!

方法1.:自定义UIImage类。
弊端:①.每次使用都需要导入 ②.老项目/大项目大量使用UIImage,替换不易。
方法2: 给UIImage添加分类。
弊端:①.在分类中方最好不要重写系统方法,一旦重写,把系统方法实现就给覆盖了。②分类中不能调用super方法。
方法3:使用runtime(交换方法)来实现。

交换方法.png

(1)给系统的方法添加分类。
(2)自己实现一个带有扩展功能的方法,如:bdf_imageNamed
(3)交换imageNamedbdf_imageNamed方法。

主要代码:

    // 获取系统方法
    // Class  _Nullable __unsafe_unretained cls 获取那个类的方法
    // SEL  _Nonnull name:获取那个方法
    Method imageNameMethod = class_getClassMethod(self, @selector(selectedImage:));
    // 获取自定义方法
    Method bdf_imageNameMethod = class_getClassMethod(self, @selector(bdf_selectedImage:));
    // 交换方法
    method_exchangeImplementations(imageNameMethod, bdf_imageNameMethod);
六.Runtime(动态添加方法)

起源:面试题中,问有没有使用过performSelector?
self performSelector:<#(SEL)#>
答:动态添加方法的时候使用过!
这里请自行查阅runtime官方文档进行了解。目前没有get到Runtime动态添加方法方法的用处~ 😂

七.Runtime(动态添加属性)

开发中什么时候需要动态添加属性?
场景1:在分类中添加属性,通过objc_setAssociatedObject方法添加属性。
场景2:给系统的类添加属性的时候,可以使用runtime动态添加属性方法。
如:

- (void)setName:(NSString *)name {
    // id  _Nonnull object 给那个对象添加属性
    // const void * _Nonnull key 属性名称
    // id  _Nullable value 属性值
    // objc_AssociationPolicy policy 保存策略
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}

上一篇 下一篇

猜你喜欢

热点阅读