Files
knowledge-kit/Chapter1 - iOS/1.82.md
2020-04-06 22:35:27 +08:00

93 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Runtime
> 做很多需求或者是技术细节验证的时候会用到 Runtime 技术,用了挺久的了,本文就写一些场景和源码分析相关的文章。
## 场景
最简单的一个场景就是防止按钮多次点击吧,比如短时间内点击了多次按钮,可以用「节流」来实现。用到的技术是 runtime。再举一个例子比如无痕埋点的实现里面对各种控件的点击、页面的跳转等也需要用到 runtime想看无痕埋点的设计与实现可以看我这篇[文章](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter1%20-%20iOS/1.55.md)。
这里不得不提的一个知识点就是为什么给类或者对象进行 hook 的操作,要放到 load 方法中进行了。
## 一、 load 和 initialize 方法
**load 方法**
对于加入运行期系统中的每个类及分类来说,必定会调用 load 方法而且仅调用1次。当包含类或分类的程序库载入系统时通常指应用程序启动的时候就会执行 load 方法。
- 类的 load 方法会在它所有父类 load 方法后调用
- 分类的 load 方法会在类本身的 load 方法后调用
- load 方法不遵循继承
- load 方法内部的实现必须简单,如果逻辑太复杂有可能会导致阻塞
load 方法有个需要注意的地方:执行该方法时,运行期系统处于脆弱状态。在执行子类的 load 方法之前,必须先执行完所有的超类的 load 方法,假如代码中还依赖了其他的程序库,那么程序库里的相关 load 方法也会先执行。在开发中load 方法中使用其他类是不安全的。
```objective-c
@implentation ClassB
+ (void)load
{
ClassA *classA = [[ClassA alloc] init];
[classA setUp];
}
@end
```
上面的代码不太推荐且不太安全,因为你没办法确定在执行 ClassB 的 load 方法的时候, ClassA 是否已经被加载到系统中。
load 方法并不像普通方法那样具备继承规则。普通的方法在面向对象程序设计中,父类的方法、属性等都会在子类中存在,假如 Person 类有 eat、sleep 方法Student 类继承子 Person 类,虽然 Person 类没有重写 eat 方法,但是你给 Student 类发送 eat 消息是可以响应的,因为方法被继承了。 Load 方法就不会,假如子类没有 load 方法,不管超类是否有 load 方法,子类都不会调用 load 方法。
load 方法内代码逻辑必须精简。因为整个应用程序在执行 load 方法的时候会被阻塞。如果 load 方法中包含繁杂的代码,那么应用程序可能会变得无法响应。更加不要使用锁。想通过 load 方法在类加载之前做些操作的,都属于错误的打开方式。它真正的用法应该是 Debug 吧,比如在 Category 中判断当前分类是否被成功 load 进去。
**inintialize 方法**
对于每个类来说,该方法会在程序首次调用该类之前调用,且只调用一次。它是由运行时系统来调用的,不应该通过代码的方式直接调用。
与 load 的区别:
- 惰性调用的。也就是说某个类的 initialize 方法也许永远不会被调用,当且仅当程序用到了该类的时候才会调用。 load 是加载进 runtime 肯定会调用initizlize 第一次使用前会被调用。对 load 来说,应用程序必须阻塞并且等着所有类 load 方法执行完毕才可以继续。
- 运行期系统在执行 initialize 方法时,是处于正常状态的,因此从运行期完整度方面来讲,此时可以安全使用并调用任何类的任意方法,而且 runtime 保证了在 initizlize 方法时期一定会在一个线程安全的环境中执行,也就是说执行 initialize 方法的这个线程可以操作类或者实例,其他线程先阻塞,等待执行完毕
- initialize 同其他方法一样,如果某个类并未实现它,而其实现了,那么就会运行超类实现的代码。
```objective-c
@implentation AClass
+ (void)initialize
{
NSLog(@"%@ initialize", self);
}
@end
@interface BClass:AClass
@end
@implentation BClass
@end
// log
AClass initialize
BClass initialize
```
基于上述特点,我们一般需要 initialize 方法中做些判断,如下
```objective-c
+ (void)initialize
{
if (self == [ACLass class]) {
// 确定了我是 AClass 的 initialize 方法执行时期
}
}
```
经常说不要在 App 中写太多的 load 方法,会影响 App 的启动时间。原因就是 load 方法的执行特点决定的。某个类的 load 方法执行是在它所有父类 load 方法执行后执行的,该类的分类的 load 方法执行是在当前类 load 方法之后执行的。如果某个类的 load 方法中引用了其他的类或者其他库的代码,则该类的 load 方法必须是其他类或者其他库中类的 load 方法执行后执行,所以类的 load 方法中最好做本类相关的逻辑,比如 runtime method swizzling。
TagPointerString 不走消息转发
CFString 走消息转发