iOS_Runtime是什麼?原理?作用?怎麼實現weak?使用

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`裏實現方法替換,方便需要的類直接調用:
// NSObject+Swizzling.h
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector;


// NSObject+Swizzling.m
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector {
  Class class = [self class];
  // 原有方法
  Method originalMethod = class_getInstanceMethod(class, originalSelector);
  // 新方法
  Method swizzledMetod = class_getInstanceMethod(class, swizzledSelector);
  // 嘗試添加`origin`方法
  BOOL didAddMethod = class_addMethod(class,
                                      originalSelector,
                                      method_getImplementation(swizzledMetod),
                                      method_getTypeEncoding(swizzledMetod));
  if (didAddMethod) { // 之前沒有實現`origin`方法
    class_replaceMethod(class,
                        swizzledSelector,
                        method_getImplementation(originalMethod),
                        method_getTypeEncoding(originalMethod));
  } else {
    method_exchangeImplementations(originalMethod, swizzledMetod);
  }
}
  • `UINavigationController`的`Category`裏替換相應的方法,並添加需要的處理
// UIViewController+Swizzling.m
+ (void)load {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    [self methodSwizzlingWithOriginalSelector:@selector(viewWillDisappear:) bySwizzledSelector:@selector(mo_viewWillDisappear:)];
  });
}

- (void)mo_viewWillDisappear:(BOOL)animated {
  [self mo_viewWillDisappear:animated];
  // 添加埋點等
}
  • `NSMutableArray`的`Category`裏攔截增刪改方法,進行判空處理 (其他的NSArray、NSDictionary、NSMutableArray、NSMutableDictionary等也可以做相應的處理,防止越界crash)
  •  類      真身

     NSArray __NSArrayI

     NSMutableArray __NSArrayM

     NSDictionary __NSDictionaryI

     NSMutableDictionary __NSDictionaryM

// NSMutableArray+Swizzling.m
+ (void)load {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    // objc_getClass("__NSArrayM") 沒有用 [self class] 處理,是因爲運行時用了`類簇` 運行時正在的類是前者
    [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(addObject:) bySwizzledSelector:@selector(safeAddObject:)];
    [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(insertObject:atIndex:) bySwizzledSelector:@selector(safeInsertObject:atIndex:)];
    [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(objectAtIndex:) bySwizzledSelector:@selector(safeObjectAtIndex:)];
    [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObjectAtIndex:) bySwizzledSelector:@selector(safeRemoveObjectAtIndex:)];
    [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObject:) bySwizzledSelector:@selector(safeRemoveObject:) ];
  });
}
- (void)safeAddObject:(id)obj {
  if (obj == nil) {
    NSLog(@"%s can add nil object into NSMutableArray", __FUNCTION__);
  } else {
    [self safeAddObject:obj];
  }
}
- (void)safeRemoveObject:(id)obj {
  if (obj == nil) {
    NSLog(@"%s call -removeObject:, but argument obj is nil", __FUNCTION__);
    return;
  }
  [self safeRemoveObject:obj];
}
- (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index {
  if (anObject == nil) {
    NSLog(@"%s can't insert nil into NSMutableArray", __FUNCTION__);
  } else if (index > self.count) {
    NSLog(@"%s index is invalid", __FUNCTION__);
  } else {
    [self safeInsertObject:anObject atIndex:index];
  }
}
- (id)safeObjectAtIndex:(NSUInteger)index {
  if (self.count == 0) {
    NSLog(@"%s can't get any object from an empty array", __FUNCTION__);
    return nil;
  }
  if (index > self.count) {
    NSLog(@"%s index out of bounds in array", __FUNCTION__);
    return nil;
  }
  return [self safeObjectAtIndex:index];
}
- (void)safeRemoveObjectAtIndex:(NSUInteger)index {
  if (self.count <= 0) {
    NSLog(@"%s can't get any object from an empty array", __FUNCTION__);
    return;
  }
  if (index >= self.count) {
    NSLog(@"%s index out of bound", __FUNCTION__);
    return;
  }
  [self safeRemoveObjectAtIndex:index];
}
  • UIButton的Category中替換 sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event  改用 afterDelay 調用,並添加標誌位,防止暴力點擊,具體代碼如下:
// UIButton+Swizzling.m
+ (void)load {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    [self methodSwizzlingWithOriginalSelector:@selector(sendAction:to:forEvent:) bySwizzledSelector:@selector(mo_SendAction:to:forEvent:)];
  });
}

// 當按鈕點擊事件 sendAction 時將會執行 mo_SendAction
- (void)mo_SendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
  if (self.isIgnore) { // 不需要被hook
    [self mo_SendAction:action to:target forEvent:event];
    return;
  }
  if ([NSStringFromClass(self.class) isEqualToString:@"UIButton"]) {
    if (self.isIgnoreEvent) {
      return;
    } else {
      self.timeInterval = self.timeInterval == 0 ? defaultInterval : self.timeInterval; // 是否自定義,否則用默認值
      [self performSelector:@selector(resetState) withObject:nil afterDelay:defaultInterval];
    }
  }
  // 此處 methodA 和 methodB方法IMP互換了,實際上執行 sendAction;所以不會死循環
  self.isIgnoreEvent = YES;
  [self mo_SendAction:action to:target forEvent:event]; // 執行系統的原有方法
}
// 還有些屬性,沒添加,詳情見Demo
  • `UILabel`的`Category`中攔截初始化方法,並設置font
// UILabel+Swizzling.m
+ (void)load {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    [self methodSwizzlingWithOriginalSelector:@selector(init) bySwizzledSelector:@selector(mo_Init)];
    [self methodSwizzlingWithOriginalSelector:@selector(initWithFrame:) bySwizzledSelector:@selector(mo_InitWithFrame:)];
    [self methodSwizzlingWithOriginalSelector:@selector(awakeFromNib) bySwizzledSelector:@selector(mo_AwakeFromNib)];
  });
}
- (instancetype)mo_Init {
  id __self = [self mo_Init];
  UIFont *font = [UIFont fontWithName:@"Zapfino" size:self.font.pointSize];
  if (font) {
    self.font = font;
  }
  return __self;
}
- (instancetype)mo_InitWithFrame:(CGRect)rect {
  id __self = [self mo_InitWithFrame:rect];
  UIFont *font = [UIFont fontWithName:@"Zapfino" size:self.font.pointSize];
  if (font) {
    self.font = font;
  }
  return __self;
}
- (void)mo_AwakeFromNib {
  [self mo_AwakeFromNib];
  UIFont *font = [UIFont fontWithName:@"Zapfino" size:self.font.pointSize];
  if (font) {
    self.font = font;
  }
}

使用Demo地址

參考1 參考2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章