当前位置:   article > 正文

iOS_Runtime是什么?原理?作用?怎么实现weak?使用_ios 热修复

ios 热修复

 

Runtime:

1、什么是runtime?

就是在程序运行的过程中,有一套C语言级别的API,它把代码从OC转换成C

2、原理:

OC是基于C,并添加了面向对象的特性,将很多静态语言在编译和链接时做的事放到了runtime运行时来处理

C:函数的调用在编译时就知道会调用哪个函数

OC:在编译的时候并不知道,只在正在运行时才会根据函数名称找到对应的函数

3、作用

获取属性、方法、成员变量、协议(包括私有的)

给分类动态添加属性、方法

字典转模型

拦截并替换方法

实现NSCoding的归档和反归档

4、Runtime是怎么实现weak?(我有在一次面试中遇到)

Runtime对注册的类会进行布局,对于weak对象会放入一个hash表中,用weak指向的`对象的内存地址`作为key。当对象的引用计数为0时会调用dealloc方法,此时会在weak表中搜索,将所有weak对象置为nil。

Key:对象内存地址 — value:n个weak对象

5、使用

  • 替换ViewController生命周期方法
  • 解决获取索引、添加、删除元素越界crash问题
  • 防止按钮重复暴力点击
  • 全局更换控件初始效果
  • App热修复
  • App异常加载占位图通用类封装
  • 全局修改导航栏返回按钮 (去掉title)

以下是使用内容:

  • `NSObject`的`Category`里实现方法替换,方便需要的类直接调用:
  1. // NSObject+Swizzling.h
  2. + (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector;
  3. // NSObject+Swizzling.m
  4. + (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector {
  5. Class class = [self class];
  6. // 原有方法
  7. Method originalMethod = class_getInstanceMethod(class, originalSelector);
  8. // 新方法
  9. Method swizzledMetod = class_getInstanceMethod(class, swizzledSelector);
  10. // 尝试添加`origin`方法
  11. BOOL didAddMethod = class_addMethod(class,
  12. originalSelector,
  13. method_getImplementation(swizzledMetod),
  14. method_getTypeEncoding(swizzledMetod));
  15. if (didAddMethod) { // 之前没有实现`origin`方法
  16. class_replaceMethod(class,
  17. swizzledSelector,
  18. method_getImplementation(originalMethod),
  19. method_getTypeEncoding(originalMethod));
  20. } else {
  21. method_exchangeImplementations(originalMethod, swizzledMetod);
  22. }
  23. }
  • `UINavigationController`的`Category`里替换相应的方法,并添加需要的处理
  1. // UIViewController+Swizzling.m
  2. + (void)load {
  3. static dispatch_once_t onceToken;
  4. dispatch_once(&onceToken, ^{
  5. [self methodSwizzlingWithOriginalSelector:@selector(viewWillDisappear:) bySwizzledSelector:@selector(mo_viewWillDisappear:)];
  6. });
  7. }
  8. - (void)mo_viewWillDisappear:(BOOL)animated {
  9. [self mo_viewWillDisappear:animated];
  10. // 添加埋点等
  11. }
  • `NSMutableArray`的`Category`里拦截增删改方法,进行判空处理 (其他的NSArray、NSDictionary、NSMutableArray、NSMutableDictionary等也可以做相应的处理,防止越界crash)
  •  类      真身

     NSArray __NSArrayI

     NSMutableArray __NSArrayM

     NSDictionary __NSDictionaryI

     NSMutableDictionary __NSDictionaryM

  1. // NSMutableArray+Swizzling.m
  2. + (void)load {
  3. static dispatch_once_t onceToken;
  4. dispatch_once(&onceToken, ^{
  5. // objc_getClass("__NSArrayM") 没有用 [self class] 处理,是因为运行时用了`类簇` 运行时正在的类是前者
  6. [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(addObject:) bySwizzledSelector:@selector(safeAddObject:)];
  7. [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(insertObject:atIndex:) bySwizzledSelector:@selector(safeInsertObject:atIndex:)];
  8. [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(objectAtIndex:) bySwizzledSelector:@selector(safeObjectAtIndex:)];
  9. [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObjectAtIndex:) bySwizzledSelector:@selector(safeRemoveObjectAtIndex:)];
  10. [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObject:) bySwizzledSelector:@selector(safeRemoveObject:) ];
  11. });
  12. }
  13. - (void)safeAddObject:(id)obj {
  14. if (obj == nil) {
  15. NSLog(@"%s can add nil object into NSMutableArray", __FUNCTION__);
  16. } else {
  17. [self safeAddObject:obj];
  18. }
  19. }
  20. - (void)safeRemoveObject:(id)obj {
  21. if (obj == nil) {
  22. NSLog(@"%s call -removeObject:, but argument obj is nil", __FUNCTION__);
  23. return;
  24. }
  25. [self safeRemoveObject:obj];
  26. }
  27. - (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index {
  28. if (anObject == nil) {
  29. NSLog(@"%s can't insert nil into NSMutableArray", __FUNCTION__);
  30. } else if (index > self.count) {
  31. NSLog(@"%s index is invalid", __FUNCTION__);
  32. } else {
  33. [self safeInsertObject:anObject atIndex:index];
  34. }
  35. }
  36. - (id)safeObjectAtIndex:(NSUInteger)index {
  37. if (self.count == 0) {
  38. NSLog(@"%s can't get any object from an empty array", __FUNCTION__);
  39. return nil;
  40. }
  41. if (index > self.count) {
  42. NSLog(@"%s index out of bounds in array", __FUNCTION__);
  43. return nil;
  44. }
  45. return [self safeObjectAtIndex:index];
  46. }
  47. - (void)safeRemoveObjectAtIndex:(NSUInteger)index {
  48. if (self.count <= 0) {
  49. NSLog(@"%s can't get any object from an empty array", __FUNCTION__);
  50. return;
  51. }
  52. if (index >= self.count) {
  53. NSLog(@"%s index out of bound", __FUNCTION__);
  54. return;
  55. }
  56. [self safeRemoveObjectAtIndex:index];
  57. }
  • UIButton的Category中替换 sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event  改用 afterDelay 调用,并添加标志位,防止暴力点击,具体代码如下:
  1. // UIButton+Swizzling.m
  2. + (void)load {
  3. static dispatch_once_t onceToken;
  4. dispatch_once(&onceToken, ^{
  5. [self methodSwizzlingWithOriginalSelector:@selector(sendAction:to:forEvent:) bySwizzledSelector:@selector(mo_sendAction:to:forEvent:)];
  6. });
  7. }
  8. // 当按钮点击事件 sendAction 时将会执行 mo_sendAction
  9. - (void)mo_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
  10. if (self.isIgnore) { // 不需要被hook
  11. [self mo_sendAction:action to:target forEvent:event];
  12. return;
  13. }
  14. if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {
  15. if (self.isIgnoreEvent) {
  16. return;
  17. } else {
  18. self.timeInterval = self.timeInterval == 0 ? defaultInterval : self.timeInterval; // 是否自定义,否则用默认值
  19. [self performSelector:@selector(resetState) withObject:nil afterDelay:defaultInterval];
  20. }
  21. }
  22. // 此处 methodA 和 methodB方法IMP互换了,实际上执行 sendAction;所以不会死循环
  23. self.isIgnoreEvent = YES;
  24. [self mo_sendAction:action to:target forEvent:event]; // 执行系统的原有方法
  25. }
  26. // 还有些属性,没添加,详情见Demo
  • `UILabel`的`Category`中拦截初始化方法,并设置font
  1. // UILabel+Swizzling.m
  2. + (void)load {
  3. static dispatch_once_t onceToken;
  4. dispatch_once(&onceToken, ^{
  5. [self methodSwizzlingWithOriginalSelector:@selector(init) bySwizzledSelector:@selector(mo_Init)];
  6. [self methodSwizzlingWithOriginalSelector:@selector(initWithFrame:) bySwizzledSelector:@selector(mo_InitWithFrame:)];
  7. [self methodSwizzlingWithOriginalSelector:@selector(awakeFromNib) bySwizzledSelector:@selector(mo_AwakeFromNib)];
  8. });
  9. }
  10. - (instancetype)mo_Init {
  11. id __self = [self mo_Init];
  12. UIFont *font = [UIFont fontWithName:@"Zapfino" size:self.font.pointSize];
  13. if (font) {
  14. self.font = font;
  15. }
  16. return __self;
  17. }
  18. - (instancetype)mo_InitWithFrame:(CGRect)rect {
  19. id __self = [self mo_InitWithFrame:rect];
  20. UIFont *font = [UIFont fontWithName:@"Zapfino" size:self.font.pointSize];
  21. if (font) {
  22. self.font = font;
  23. }
  24. return __self;
  25. }
  26. - (void)mo_AwakeFromNib {
  27. [self mo_AwakeFromNib];
  28. UIFont *font = [UIFont fontWithName:@"Zapfino" size:self.font.pointSize];
  29. if (font) {
  30. self.font = font;
  31. }
  32. }

使用Demo地址

参考1 参考2

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/428290
推荐阅读
相关标签
  

闽ICP备14008679号