原文链接:https://www.jianshu.com/p/1c6b4887674b
提到懒加载,或许99%搞iOS开发的同学都会用过,众所周知,懒加载就是用到的时候再加载,通常我们都用作属性上面,通过self.xx来实现。那么OC方法的懒加载你听说过吗?用过吗?下面就是通过runtime来实现一下方法的懒加载,玩一下。
首先创建一个Person类,.h文件里面什么都不用写
ViewController中导入头文件开始玩
viewdidload方法中初始化person并调用person的eat方法,此时可以看到xcode编译警告eat方法,如果我们现在运行代码,就会crash,因为找不到这个方法,找到Person.m
Objective C 提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个 selector 提供实现。我们只要实现 +resolveInstanceMethod: 或 +resolveClassMethod: 方法,并在其中为指定的 selector 提供实现即可(通过调用运行时函数 class_addMethod 来添加)。这两个方法都是 NSObject 中的类方法,其原型为:
+ (BOOL)resolveClassMethod:(SEL)name;
+ (BOOL)resolveInstanceMethod:(SEL)name;
简单来说就是,当这个类被调用了没有实现的方法,就会进入该方法来。
参数 name 是需要被动态决议的 selector;返回值文档中说是表示动态决议成功与否。但在上面的例子中(不涉及消息转发的情况下),如果在该函数内为指定的 selector 提供实现,无论返回 YES 还是 NO,编译运行都是正确的;但如果在该函数内并不真正为 selector 提供实现,无论返回 YES 还是 NO,运行都会 crash,道理很简单,selector 并没有对应的实现,而又没有实现消息转发。
resolveInstanceMethod 是为对象方法进行决议,
而 resolveClassMethod 是为类方法进行决议。
先简单运行一下代码,打印一下SEL看看方法是什么
可以清楚的看到就是之前调用的eat,接下来用动态方法决议手段来修改上面的代码:
解析:
这个函数里面有4个参数,我们打开帮助看看官方是怎么说的。
参数1,需要添加方法的类。[Pseron class]
参数2,需要添加的方法,就是参数sel,刚才NSLog过了,就是eat
参数3就比较复杂了,书面翻译是需要填入一个函数,而这个函数需要有两个参数一个是self,一个是_cmd。那么我们可以返回刚才的实现代码看一下,这里又get了一个知识点就是,所有的C语言的函数里面,都有这两个隐式参数,只要调用系统都会传递进来。然后继续看官方文档,class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:"); 这是官方给出的调用方式。 可以看到,参数3是用(IMP)强转过的函数,所以参数3就是(IMP)eat
参数4,字面解释是描述该方法参数类型的字符数组。可能的值,看Objective-C Runtime Programming Guide > Type Encodings。因为函数必须至少有两个参数自_cmd,第二和第三个字符必须是"@:"(第一个字是返回类型)。打开文档,可以看到
官方文档
因为我们的eat返回值是void,所以使用"v",加上官方要求的值放在一起就是"v@:"。
接下来我们实现一下带参数的方法
[p performSelector:@selector(play:) withObject:@"王者荣耀"];
带参数的方法
我们在参数四字符串后面继续拼接参数就好。