iOS中的Runtime學習小記

本博客主要分以下幾個方面來介紹iOS中的runtime

  • Runtime的概念介紹
  • iOS中的消息機制
  • Runtime的作用

Objective-C語言中的Runtime概念

  1. 動態編程語言和靜態編程語言的區別
  • 動態編程語言:在程序運行過程中可以改變數據類型的結構,對象的函數,變量可以被修改刪除。例如OC
  • 靜態編程語言:在程序編譯階段檢查數據的類型,數據類型的結構不可以在運行時被改變。例如C
  1. Runtime---運行時系統是一個有公開接口的動態庫,由一些數據結構和函數的集合組成,這些數據結構和函數的聲明頭文件,在/usr/include/objc,這些函數支持用純C的函數來實現和Objective-C同樣的功能
  2. Runtime---是開源的,目前蘋果公司和GNU各自維護一個開源的runtime版本,apple open runtime;
  3. Runtime---在OC中的使用方式
  • 通過Objective-C源代碼
  • 通過類NSObject的方法
class 返回對象的類
isKindeOfClass:和isMemberOfClass: 檢查對象是否在指定的類繼承體系中
respondsToSelector: 檢查對象能否響應指定的消息
conformsToProtocol: 檢查對象是否實現了指定協議類的方法
methodForSelector: 返回指定方法實現的地址
  • 通過運行時系統的函數
    通過導入頭文件"#import<objc/message.h>"調用相關函數

iOS中的消息機制

  1. Objective-C中消息機制的相關概念
  • message(消息) --包括了函數名+參數列表的一種抽象
  • method(方法)-- 是真正的存在的代碼。如:- (int)meaning { return 42; }
  • selector(方法選擇器)--通過SEL類型存在,描述一個特定的method 或者說 message。在實際編程中,可以通過selector進行檢索方法等操作
  • SEL(方法選擇器) -- 是一個char*指針,僅僅表示它所代表的方法名字,SEL只是一個指向方法的指針(準確的說,只是一個根據方法名hash化了的KEY值,能唯一代表一個方法),它的存在只是為了加快方法的查詢速度
 //SEL
 SEL selector = @selector(message); //@selector不是函數調用,只是給這個坑爹的編譯器的一個提示   
  NSLog (@"%s", (char *)selector); //print message   
  //以下函數命名方式這被認為是一種編譯錯誤
 -(void)setWidth:(int)width;   
 -(void)setWidth:(double)width;
  • IMP --函數指針
    • 我們可以通過SEL方便、快速、準確的獲得它所對應的IMP(也就是函數指針),而在取得了函數指針之后,也就意味著我們取得了執行的時候的這段方法的代碼的入口,這樣我們就可以像普通的C語言函數調用一樣使用這個函數指針。當然我們可以把函數指針作為參數傳遞到其他的方法,或者實例變量里面,從而獲得極大的動態性

Runtime的作用

  1. 關聯對象:主要為分類增加屬性和實例變量。

    能用擴展一般不用繼承,因為隨著繼承深度的增加,代碼可維護性變差

    static char myKey;  //定義一個靜態字符
    /*!
     *  給關聯對象賦值
     *
     *  @param self 需要添加關聯的分類
     *  @param key  const void *,key僅僅是一個地址,不是字符串內容。具體指向內容不用關心
     *  @param value 關聯對象的值
     *  @param policy 關聯策略,類似于對象的屬性修飾符,OBJC_ASSOCIATION_RETAIN_NONATOMIC等
     *
     */
     objc_setAssociatedObject(self, &myKey, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    //給關聯對象賦值
    /*!
     *  根據關聯的key,獲取關聯的值
     *
     *  @param self  需要添加關聯的分類
     *  @param myKey onst void *,key僅僅是一個地址,不是字符串內容。具體指向內容不用關心
     *
     *  @return 關聯的對象的值
     */
     objc_getAssociatedObject(self, &myKey);
    
  2. 獲取對象的私有變量,私有方法,動態添加屬性、方法等。

  • 獲取類名
```objectivec
Class class = [objc class];
class_getName(class);
class_getSuperclass(class);
```
  • 獲取成員變量(可以獲取私有成員變量)
CaculatorMaker *make = [[CaculatorMaker alloc] init];
Class objcClass = [make class];
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(objcClass, &outCount);
for (int i = 0; i<outCount; i++) {
   Ivar ivar = ivars[i];
   const char *ivarString = ivar_getName(ivar);
   NSLog(@"the variable of make is %s",ivarString);
 }
 free(ivars);
  • 獲取屬性
/*!
 *  獲取對象的所有屬性和屬性值
 *
 *  @param object 對象
 *
 *  @return 屬性和屬性值數組
*/
- (NSMutableArray *)getObjectPropertyAndValues:(id)object
{
     NSMutableArray *array = [NSMutableArray new];
     Class objcClass = [object class];
     unsigned int outCount = 0 ;
     objc_property_t *properties = class_copyPropertyList(objcClass, &outCount);
     for (int i = 0; i < outCount; i++) {
       objc_property_t property = properties[i];
       const char *propertyName = property_getName(property);
       NSString *propertyKey = [NSString stringWithUTF8String:propertyName];
       NSString *propertyValue = [object valueForKey:propertyKey];
       RACTuple *tuple = RACTuplePack(propertyKey,propertyValue);
       [array addObject:tuple];
     }
     return array;
   }
  • 動態添加方法
 CaculatorMaker *make = [[CaculatorMaker alloc] init];
 /*!
  *  runtime添加方法
  *
  *  @param class     被添加方法的類
  *  @param addMethod: 方法的名稱
  *  @param imp:  實現這個方法的函數
  *  @param type: 定義函數返回值類型和參數類型的字符串[Type Encodings](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)
  *  @return 添加成功或者失敗
  */
 class_addMethod([make class], @selector(addMethod:), (IMP)addMethod, "i@:@");
 [make performSelector:@selector(addMethod:) withObject:@"new Method"];

 //添加的方法的具體實現
 int addMethod(id self, SEL _cmd, NSString *str)
 {
 NSLog(@"new method is %@",str);
 return 100;
 }
  • 動態添加屬性和成員變量均未成功,屬性添加完成之后的setter方法和getter方法不會實現。
  1. 消息轉發(message forwarding)-當一個對象無法接收某一消息時,就會啟動消息轉發機制。
    通過這一機制,我們可以告訴對象如何處理未知的消息。默認情況下,對象接收到未知的消息,會導致程序崩潰。
  • 消息轉發的步驟:1.動態方法解析 2.備用接收者 3. 完整轉發
  • 動態方法解析:當對象接收到未實現的方法時,為自動調用resolveInstanceMethod:和resolveClassMethod:
//當對象接收到未知方法時,動態添加以下方法。作為默認執行,防止程序崩潰
 void functionForMethod1(id self, SEL _cmd)
 {
   NSLog(@"當未實現某個方法時默認執行此函數");
 }
 + (BOOL)resolveInstanceMethod:(SEL)sel
 {
   class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:");
   return [super resolveInstanceMethod:sel];
 }

 + (BOOL)resolveClassMethod:(SEL)sel
 {
   class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:");
   return [super resolveClassMethod:sel];
 }
 ```
+ 備用接收者:如果沒有用動態方法解析處理消息,則Runtime會繼續調以下方法:

```objectivec
//如果對象實現了這個方法,并返回一個非nil的值,則這個對象會作為消息的接收者。且消息會被分發到這個對象
- (id)forwardingTargetForSelector:(SEL)aSelector {
 id  standByObj;
 //....standByObj 備用接收對象的初始化等操作
 return standByObj;
return [super forwardingTargetForSelector:aSelector];
 }

  • 完整轉發:如果在上一步還不能處理未知消息,則唯一能做的就是啟用完整的消息轉發機制了
/*!
 *  消息轉發機制使用從這個方法中獲取的信息來創建NSInvocation對象。因此我們必須重寫這個方法,為給定的selector提供一個合適的方法簽名
 *
 *  @param aSelector 需要轉發的方法
 *
 *  @return 新的方法簽名
 */

 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
 {
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];

      if (!signature) {
      if ([SUTRuntimeMethodHelper instancesRespondToSelector:aSelector]) {

          signature = [SUTRuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
      }
    }
    return signature;
  }

  /*!
  *  將消息轉發給其它對象
  *
  *  @param anInvocation 需要轉發的消息的selector,目標(target)和參數
  */
  - (void)forwardInvocation:(NSInvocation *)anInvocation
  {   
    if ([SUTRuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {

      [anInvocation invokeWithTarget:_helper];
    }
  }
  1. 方法交換(Method Swizzling)
  • 給view controller的viewWillAppear:中添加跟蹤代碼,將viewWillAppear:與自定義的dg_viewWillAppear:交換,代碼如下:
#import "UIViewController+Tracking.h"
#import <objc/runtime.h>
@implementation UIViewController (Tracking)
/*!
*  在load中實現方法交換,
*/
+ (void)load
{
   //
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken,^{
       Class class = [self class];
       SEL originalSelector = @selector(viewWillAppear:);
       SEL swizzledSelector = @selector(dg_viewWillAppear:);
       Method originalMethod = class_getInstanceMethod(class, originalSelector);
       Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
       //本類是否已添加swizzledMethod,未添加則進行添加
       BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

       if (didAddMethod) {
           class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
           } else{
             method_exchangeImplementations(originalMethod, swizzledMethod);
           }
           });
         }
 - (void)dg_viewWillAppear:(BOOL)animated
 {
   [self dg_viewWillAppear:animated];
   NSLog(@"viewWilleAppear");
 }
 @end

參考

  1. Objective-C Runtime 運行時
  2. 讓你快速上手Runtime
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容

  • 這篇文章完全是基于南峰子老師博客的轉載 這篇文章完全是基于南峰子老師博客的轉載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,578評論 33 466
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,751評論 0 9
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,148評論 0 9
  • objc_getAssociatedObject返回與給定鍵的特定對象關聯的值。ID objc_getAssoci...
    有一種再見叫青春閱讀 1,607評論 0 7
  • 畫了一幅畫,寄給遠方的朋友。 收到張同學的信件,是在一個陰雨的下午,南方的冬天總是伴隨著這樣的細雨綿綿。 他是個文...
    盛晴雨閱讀 437評論 0 1