成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS面試題·項目中用過 Runtime 嗎?

移動開發
我們知道靜態語言在編譯時期,就已經確定了函數的具體調用,而動態語言要等到運行時期才能真正確定調用哪個函數; Objective-C 是一門動態語言,它是通過 Runtime 這個運行時機制來實現的。

前言

我們知道靜態語言在編譯時期,就已經確定了函數的具體調用,而動態語言要等到運行時期才能真正確定調用哪個函數; Objective-C 是一門動態語言,它是通過 Runtime 這個運行時機制來實現的。

[[248751]]

雖然說 Runtime 是相對于底層的機制,但是在項目過程中也經常用來解決一些問題。下面我們就來看看利用 Runtime 可以解決項目中什么問題。

項目中用 Runtime 實現的功能

利用關聯對象為分類增加偽屬性

在項目的開發中,經常會遇到要為已經存在的類添加屬性。面對這種情況,我們一般都是創建一個分類,來實現為已有的類增加屬性,但是由于分類結構的特殊性,在分類添加屬性,并不會為我們自動創建實例變量和存儲方法。

首先我們要知道,常規定義一個 @property,其實編譯器會為我們做三件事情:

  • 生成實例變量 _property
  • 生成 getter 方法
  • 生成 setter 方法

但是,在分類中并不會幫我們去生成實例變量和存取方法,所以我們需要自己去實現存取方法,這里我們會通過關聯對象去將鍵值關聯到對象上面去,以下是代碼示例: 

  1. @property (nonatomic, strong) NSString *title; 
  2.  
  3. - (NSString *)title { 
  4.     return objc_getAssociatedObject(self, _cmd); 
  5.  
  6. - (void)setTitle:(NSString *)title { 
  7.     objc_setAssociatedObject(self, @selector(title), title, OBJC_ASSOCIATION_RETAIN); 

這個我們暫時只講如何通過關聯對象為分類增加偽屬性,至于分類為什么不會為我們自動添加實例變量和存取方法,以及關聯對象的實現原理等,我們會在后面的面試題繼續涉及到這一話題。

利用 Method Swizzling 交換方法

我們可以用 Method Swizzling 來交換兩個方法的實現,以便達到 Hook 的效果;例如交換 ViewController 生命周期方法來實現頁面埋點,或者在不影響原有的功能增加一些特殊的功能。

交換方法主要是利用到 Runtime 中的class_addMethod 、class_replaceMethod、method_exchangeImplementations 方法來實現的,以下是 Method Swizzling 代碼示例: 

  1. /** 
  2.  交換方法 
  3.  */ 
  4. + (void)pxy_swizzleMethodWithOriginalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector { 
  5.     Class class = [self class]; 
  6.  
  7.     SEL originalSeletor = originalSelector; 
  8.     SEL swizzledSeletor = swizzledSelector; 
  9.  
  10.     Method originMethod = class_getInstanceMethod(class, originalSeletor); 
  11.     Method swizzledMethod = class_getInstanceMethod(class, swizzledSeletor); 
  12.  
  13.     //先嘗試給源SEL添加IMP,這里是為了避免源SEL沒有實現IMP的情況 
  14.     BOOL didAddMethod = class_addMethod(class, originalSeletor, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); 
  15.     if (didAddMethod) { 
  16.         //添加成功:說明源SEL沒有實現IMP,將源SEL的IMP替換到交換SEL的IMP 
  17.         class_replaceMethod(class, swizzledSeletor, method_getImplementation(originMethod), method_getTypeEncoding(originMethod)); 
  18.     } else { 
  19.         //添加失敗:說明源SEL已經有IMP,直接將兩個SEL的IMP交換即可 
  20.         method_exchangeImplementations(originMethod, swizzledMethod); 
  21.     } 

利用 class_copyIvarList 實現 NSCoding 的自動歸檔解檔

在利用 NSKeyedArchiver 歸檔解檔對象的時候,對象 Model 需要實現 NSCoding 協議,并且要實現 encodeWithCoder、initWithCoder 兩個方法,在這兩個方法中要為每個屬性進行 code 和 encode,不然就會 crash。

在項目開發過程中,經常會出現 Model 中的屬性會變更,這個時候總是會忘記去修改對應的屬性 code 和 encode,這里就會導致 crash;為了避免這個現象和讓 Model 中的方法更加簡潔可控,這里我們會利用 class_copyIvarList 來獲取對象中的成員變量列表,然后利用 KVC 來 code 和 encode。實例代碼如下:(這里我們將這個通用的代碼抽象成宏,這樣子在需要的 Model 中直接調用就可以了) 

  1. #define PXYNSCodingRuntime_EncodeWithCoder(Class) \ 
  2. unsigned int outCount = 0;\ 
  3. Ivar *ivars = class_copyIvarList([Class class], &outCount);\ 
  4. for (int i = 0; i < outCount; i++) {\ 
  5.     Ivar ivar = ivars[i];\ 
  6.     NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];\ 
  7.     [aCoder encodeObject:[self valueForKey:key] forKey:key];\ 
  8. }\ 
  9. free(ivars);\ 
  10.  
  11. #define PXYNSCodingRuntime_InitWithCoder(Class)\ 
  12. if (self = [super init]) {\ 
  13.     unsigned int outCount = 0;\ 
  14.     Ivar *ivars = class_copyIvarList([Class class], &outCount);\ 
  15.     for (int i = 0; i < outCount; i++) {\ 
  16.         Ivar ivar = ivars[i];\ 
  17.         NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];\ 
  18.         id value = [aDecoder decodeObjectForKey:key];\ 
  19.         if (value) {\ 
  20.             [self setValue:value forKey:key];\ 
  21.         }\ 
  22.     }\ 
  23.     free(ivars);\ 
  24. }\ 
  25. return self;\ 
  26.  
  27. // 對應調用 
  28. - (void)encodeWithCoder:(NSCoder *)aCoder { 
  29.     PXYNSCodingRuntime_EncodeWithCoder(Father) 
  30. - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { 
  31.     PXYNSCodingRuntime_InitWithCoder(Father) 

利用 objc_allocateClassPair、object_setClass 等 API 來實現 KVO Block

在項目中,會經常使用 KVO 來監聽某個屬性的變化。先給出系統調用的方式,添加監聽后,在 observeValueForKeyPath 方法中處理變化: 

  1. - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context; 
  2.  
  3.  - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { 
  4.  NSLog(@"%@ 對象的 %@ 屬性改變了:%@",object,keyPath,change); 
  5.  } 

但是在開發過程中,有時候想將代碼增加內聚性和在 observeValueForKeyPath 減少判斷,我們可以通過 Runtime 來實現一個 KVO Block,這樣調用地方即處理消息的地方,代碼上比較直觀,簡單 API 如下: 

  1. typedef void(^PXYKVOCompleteBlock)(id observer, NSString *keyPath, id oldValue, id newValue); 
  2.  
  3. /** 
  4.  添加 KVO Block 
  5.  */ 
  6. - (void)pxy_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath completeBlock:(PXYKVOCompleteBlock)completeBlock; 
  7.  
  8. /** 
  9.  移除 KVO Block 
  10.  */ 
  11. - (void)pxy_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath; 

KVO 主要是動態派生出一個中間類,然后在這個中間類處理相關通知邏輯,具體代碼可以 Demo 中的 NSObject+PXYKVO 具體實現;

利用消息轉發機制實現多播委托(蹦床模式)

首先,在對象收到無法處理的消息之后,會執行消息轉發,消息轉發有三個步驟:

  • 調用 resolveInstanceMethod 方法。動態方法解析,這里會給類使用 class_addMethod 來增加方法的機會。
  • 調用 forwardingTargetForSelector 方法,看是否有備用接收者,將消息轉發給備用接收者處理。
  • 調用 methodSignatureForSelector 和 forwardInvocation 方法,進行完成的消息轉發。

如果經過上面三個步驟,還不能正確處理消息,程序就會走 doesNotRecognizeSelector 方法,crash 掉。

蹦床模式:就是把一條消息 “反彈” 到另外一個對象,蹦床一般使用 forwardInvocation 來實現。

在項目開發中,事件回調一般使用:Block、Delegate、NSNotificationCenter;但是在多個模塊需要監聽一個事件的場景:使用通知會將項目變得不可控,因為任何一個地方都可以監聽這個通知,在排查問題的時候就會變得異常困難,這個時候我們可以使用多播委托,實現一對多回調。

大致原理:實現一個管理類,將需要回調的對象注冊進來,然后將事件消息發送給這個管理類,由于這個管理類是沒有實現委托方法的,就不能正常處理這個消息,這個時候就會走消息轉發流程;然后我們通過消息轉發流程,將消息轉發到注冊進來的對象中去,這樣子就要可以實現我們的多播委托了。

具體代碼可以看 Demo 中的 PXYMulticastDelegate 多播委托實現類。

總結

Objective-C 利用 Runtime 運行時變成一門動態語言,在開發過程中,使用 Runtime 相關 API 可以實現一些很強大的功能,這里我們簡單講到使用 Runtime 完成為分類增加偽屬性、利用 Method SWizzling 來 Hook 方法、實現 NSCoding 自動歸檔解檔、實現 KVO Block、多播委托。

當然還可以實現更多的功能,比如字典模型之間的轉換、頁面無侵入埋點、監聽 App 網絡流量等等。

還有可以實現什么好玩的功能,歡迎留言,感激不盡。

責任編輯:未麗燕 來源: 簡書
相關推薦

2022-07-12 12:05:22

JavaSemaphore

2020-06-04 14:40:40

面試題Vue前端

2016-03-03 10:07:39

ios內存管理面試總結

2023-11-13 07:37:36

JS面試題線程

2011-03-24 13:27:37

SQL

2024-04-01 00:00:00

Redis緩存服務消息隊列

2021-08-05 05:04:50

熱部署模型字節

2009-06-06 18:34:05

java面試題

2009-06-06 18:36:02

java面試題

2014-09-19 11:17:48

面試題

2015-09-02 09:32:56

java線程面試

2019-11-26 10:30:11

CSS前端面試題

2023-07-28 08:04:56

StringHeaatomic線程

2025-02-26 07:58:41

2024-06-04 14:52:28

2023-07-14 08:12:21

計時器unsafecontext

2013-01-05 14:51:34

JavaScriptjQuery面試

2014-07-28 14:00:40

linux面試題

2018-03-08 18:40:47

Java百度面試題

2009-06-16 14:03:16

Hibernate面試Hibernate面試
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品美女www爽爽爽 | 免费av观看 | 亚洲欧美一区二区三区在线 | 国产精品久久久久久久久大全 | 四色成人av永久网址 | 成人在线一级片 | www.国产日本| 中文字幕视频一区二区 | 欧美日韩第一页 | 免费av毛片 | 久久久久一区二区 | 欧美综合久久 | 黄色免费观看 | 国产视频精品区 | 日韩视频91 | av一区在线| 成人3d动漫一区二区三区91 | 欧美一级久久 | 成年人在线视频 | 亚洲成av人片在线观看无码 | 亚洲 中文 欧美 日韩 在线观看 | 日韩高清www | 国产日韩欧美中文 | 国产麻豆乱码精品一区二区三区 | 黄色网址在线播放 | 欧美亚洲激情 | 欧美成人精品激情在线观看 | 365夜爽爽欧美性午夜免费视频 | 一区二区三区视频在线观看 | 国产三级精品三级在线观看四季网 | 91精品国产91久久久久福利 | 日韩中文电影 | 亚洲精品中文字幕在线 | 中文在线а√在线8 | 国产片侵犯亲女视频播放 | 欧美在线日韩 | 亚州精品天堂中文字幕 | 午夜寂寞影院列表 | 超碰在线影院 | 正在播放国产精品 | 欧美自拍日韩 |