1. 程式人生 > >Objective C執行時(runtime)

Objective C執行時(runtime)

前言:

Objective C的runtime技術功能非常強大,能夠在執行時獲取並修改類的各種資訊,包括獲取方法列表、屬性列表、變數列表,修改方法、屬性,增加方法,屬性等等,本文對相關的幾個要點做了一個小結。

目錄:

  (6) 總結

(1)在執行時對函式進行動態替換 : class_replaceMethod

      使用該函式可以在執行時動態替換某個類的函式實現,這樣做有什麼用呢?最起碼,可以實現類似windows上hook效果,即截獲系統類的某個例項函式,然後塞一些自己的東西進去,比如打個log什麼的。

示例程式碼:

複製程式碼
IMP orginIMP;
NSString * MyUppercaseString(id SELF, SEL _cmd)
{
    NSLog(@"begin uppercaseString");
    NSString *str = orginIMP (SELF, _cmd);(3)
    NSLog(@"end uppercaseString");
    return str;
}
-(void)testReplaceMethod
{
      Class strcls = [NSString class];
      SEL  oriUppercaseString = @selector(uppercaseString);
      orginIMP = [NSStringinstanceMethodForSelector:oriUppercaseString];  (1)  
      IMP imp2 = class_replaceMethod(strcls,oriUppercaseString,(IMP)MyUppercaseString,NULL);(2)
      NSString *s = "hello world";
      NSLog(@"%@",[s uppercaseString]];
}
複製程式碼

執行結果為:

begin uppercaseString

end uppercaseString

HELLO WORLD

這段程式碼的作用就是

(1)得到uppercaseString這個函式的函式指標存到變數orginIMP中

(2)將NSString類中的uppercaseString函式的實現替換為自己定義的MyUppercaseString

(3)在MyUppercaseString中,先執行了自己的log程式碼,然後再呼叫之前儲存的uppercaseString的系統實現,這樣就在系統函式執行之前加入自己的東西,後面每次對NSString呼叫uppercaseString的時候,都會打印出log來

 與class_replaceMethod相仿,class_addMethod可以在執行時為類增加一個函式。

(2)當某個物件不能接受某個selector時,將對該selector的呼叫轉發給另一個物件- (id)forwardingTargetForSelector:(SEL)aSelector

     forwardingTargetForSelector是NSObject的函式,使用者可以在派生類中對其過載,從而將無法處理的selector轉發給另一個物件。還是以上面的uppercaseString為例,如果使用者自己定義的CA類的物件a,沒有uppercaseString這樣一個例項函式,那麼在不呼叫respondSelector的情況下,直接執行[a performSelector:@selector"uppercaseString"],那麼執行時一定會crash,此時,如果CA實現了forwardingTargetForSelector函式,並返回一個NSString物件,那麼就相對於對該NSString物件執行了uppercaseString函式,此時就不會crash了。當然實現這個函式的目的並不僅僅是為了程式不crash那麼簡單,在實現裝飾者模式時,也可以使用該函式進行訊息轉發。

示例程式碼:

複製程式碼
 1 @interface CA : NSObject
 3 -(void)f;
 4 
 5 @end
 6 
 7 @implementation CA
 8 
 9 - (id)forwardingTargetForSelector:(SEL)aSelector
11 {
13     if (aSelector == @selector(uppercaseString))
15     {
17         [email protected]"hello world";
19     }
21 }
複製程式碼

測試程式碼:

CA *a = [CA new];

 NSString * s = [a performSelector:@selector(uppercaseString)];

NSLog(@"%@",s);

測試程式碼的輸出為:HELLO WORLD 

 ps:這裡有個問題,CA類的物件不能接收@selector(uppercaseString),那麼如果我在forwardingTargetForSelector函式中用class_addMethod給CA類增加一個uppercaseString函式,然後返回self,可行嗎?經過試驗,這樣會crash,此時CA類其實已經有了uppercaseString函式,但是不知道為什麼不能呼叫,如果此時new一個CA類的物件,並返回,是可以成功的。

(3)當某個物件不能接受某個selector時,向物件所屬的類動態新增所需的selector

+ (BOOL) resolveInstanceMethod:(SEL)aSEL 

     這個函式與forwardingTargetForSelector類似,都會在物件不能接受某個selector時觸發,執行起來略有差別。前者的目的主要在於給客戶一個機會來向該物件新增所需的selector,後者的目的在於允許使用者將selector轉發給另一個物件。另外觸發時機也不完全一樣,該函式是個類函式,在程式剛啟動,介面尚未顯示出時,就會被呼叫。

     在類不能處理某個selector的情況下,如果類過載了該函式,並使用class_addMethod添加了相應的selector,並返回YES,那麼後面forwardingTargetForSelector就不會被呼叫,如果在該函式中沒有新增相應的selector,那麼不管返回什麼,後面都會繼續呼叫forwardingTargetForSelector,如果在forwardingTargetForSelector並未返回能接受該selector的物件,那麼resolveInstanceMethod會再次被觸發,這一次,如果仍然不新增selector,程式就會報異常

複製程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 程式碼示例一: @implementation CA void dynamicMethodIMP(id selfSEL _cmd) 5 {    7      printf("SEL %s did not exist\n",sel_getName(_cmd)); 9 } 10 11 + (BOOL) resolveInstanceMethod:(SEL)aSEL 13 { 15     if (aSEL == @selector(t)) 17     { 19         class_addMethod([selfclass], aSEL, (IMP) dynamicMethodIMP, "[email protected]:"); 21         return YES; 23     } 25     return [superresolveInstanceMethod:aSEL]; 27 } 28 29 @end 測試程式碼: CA * ca = [CA new] [ca performSelector:@selector(t)]; 
複製程式碼

執行結果

   SEL t did not exist

複製程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 示例程式碼二: @implementation CA void dynamicMethodIMP(id selfSEL _cmd) { printf("SEL %s did not exist\n",sel_getName(_cmd)); } + (BOOL) resolveInstanceMethod:(SEL)aSEL { return  YES; } - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(uppercaseString)) { return @"hello world"; } } 測試程式碼 : a = [[CA alloc]init]; NSLog(@"%@",[a performSelector:@selector(uppercaseString)];
複製程式碼

該測試程式碼的輸出為:HELLO WORLD 

對於該測試程式碼,由於a沒有uppercaseString函式,因此會觸發resolveInstanceMethod,但是由於該函式並沒有新增selector,因此執行時發現找不到該函式,會觸發

forwardingTargetForSelector函式,在forwardingTargetForSelector函式中,返回了一個NSString "hello world",因此會由該string來執行uppercaseString函式,最終返回大寫的hello world。

複製程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 示例程式碼三: @implementation CA + (BOOL) resolveInstanceMethod:(SEL)aSEL { return  YES; } - (id)forwardingTargetForSelector:(SEL)aSelector { return nil; }  測試程式碼: 1  a = [[CA alloc]init]; 2  NSLog(@"%@",[a performSelector:@selector(uppercaseString)];
複製程式碼

這段程式碼的執行順序為:

 (1):首先在程式剛執行,AppDelegate都還沒有出來時,resolveInstanceMethod就被觸發,

(2)等測試程式碼執行時,forwardingTargetForSelector被呼叫

(3)由於forwardingTargetForSelector返回了nil,因此執行時還是找不到uppercaseString selector,這時又會觸發resolveInstanceMethod,由於還是沒有加入selector,於是會crash。

(4) 使用class_copyPropertyList及property_getName獲取類的屬性列表及每個屬性的名稱

複製程式碼
u_int               count; objc_property_t*    properties= class_copyPropertyList([UIView class], &count); for (int i = 0; i < count ; i++) { const char* propertyName = property_getName(properties[i]); NSString *strName = [NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]; NSLog(@"%@",strName); }
複製程式碼

 以上程式碼獲取了UIView的所有屬性並列印屬性名稱, 輸出結果為:

複製程式碼
skipsSubviewEnumeration viewTraversalMark viewDelegate monitorsSubtree backgroundColorSystemColorName gesturesEnabled deliversTouchesForGesturesToSuperview userInteractionEnabled tag layer _boundsWidthVariable _boundsHeightVariable _minXVariable _minYVariable _internalConstraints _dependentConstraints _constraintsExceptingSubviewAutoresizingConstraints _shouldArchiveUIAppearanceTags
複製程式碼

(5) 使用class_copyMethodList獲取類的所有方法列表

    獲取到的資料是一個Method陣列,Method資料結構中包含了函式的名稱、引數、返回值等資訊,以下程式碼以獲取名稱為例:

複製程式碼
u_int               count; Method*    methods= class_copyMethodList([UIView class], &count); for (int i = 0; i < count ; i++) { SEL name = method_getName(methods[i]); NSString *strName = [NSString  stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding]; NSLog(@"%@",strName); }
複製程式碼

  程式碼執行後將輸出UIView所有函式的名稱,具體結果略。

     其他一些相關函式:

複製程式碼
1.SEL method_getName(Method m) 由Method得到SEL
2.IMP method_getImplementation(Method m)  由Method得到IMP函式指標
3.const char *method_getTypeEncoding(Method m)  由Method得到型別編碼資訊
4.unsigned int method_getNumberOfArguments(Method m)獲取引數個數
5.char *method_copyReturnType(Method m)  得到返回值型別名稱
6.IMP method_setImplementation(Method m, IMP imp)  為該方法設定一個新的實現
複製程式碼

(6)總結

       總而言之,使用runtime技術能做些什麼事情呢?

       可以在執行時,在不繼承也不category的情況下,為各種類(包括系統的類)做很多操作,具體包括:

  •    增加
增加函式:class_addMethod 增加例項變數:class_addIvar 增加屬性:@dynamic標籤,或者class_addMethod,因為屬性其實就是由getter和setter函式組成 增加Protocol:class_addProtocol (說實話我真不知道動態增加一個protocol有什麼用,-_-!!)
  •    獲取  
獲取函式列表及每個函式的資訊(函式指標、函式名等等):class_getClassMethod method_getName ... 獲取屬性列表及每個屬性的資訊:class_copyPropertyList property_getName 獲取類本身的資訊,如類名等:class_getName class_getInstanceSize 獲取變數列表及變數資訊:class_copyIvarList 獲取變數的值
  •    替換
將例項替換成另一個類:object_setClass 將函式替換成一個函式實現:class_replaceMethod 直接通過char *格式的名稱來修改變數的值,而不是通過變數

相關推薦

Objective C執行runtime

前言: Objective C的runtime技術功能非常強大,能夠在執行時獲取並修改類的各種資訊,包括獲取方法列表、屬性列表、變數列表,修改方法、屬性,增加方法,屬性等等,本文對相關的幾個要點做了一個小結。 目錄:   (6) 總結 (1)在執行時對函式進行動態替換 : 

Objective C執行runtime技術的幾個要點總結

前言: Objective C的runtime技術功能非常強大,能夠在執行時獲取並修改類的各種資訊,包括獲取方法列表、屬性列表、變數列表,修改方法、屬性,增加方法,屬性等等,本文對相關的幾個要點做了一個小結。 目錄:   (6) 總結 (1)在執行時對函式進行動態替換 : cl

執行runtime-方法交換

1> 建立一個 Person 類,並定義兩個方法 study 和 run,分別實現 #import "Person.h" @implementation Person - (void)study { NSLog(@"study"); } -

Java 執行RUNTIME註解詳解

整理測試後並附上完整程式碼 註解定義 註解(Annotation),也叫元資料。一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元

iOS執行runtime探究一:重要概念

iOS執行時簡介 因為Objc是一門動態語言,所以它總是想辦法把一些決定工作從編譯連線推遲到執行時。也就是說只有編譯器是不夠的,還需要一個執行時系統 (runtime system) 來執行編譯後的程式碼。這就是 Objective-C Runtime 系統存

Go 運行runtime

垃圾 .com map cgo 一個 也不會 bsp 部分 targe 盡管 Go 編譯器產生的是本地可執行代碼,這些代碼仍舊運行在 Go 的 runtime(這部分的代碼可以在 runtime 包中找到)當中。這個 runtime 類似 Java 和 .NET 語言所用到

Objective-C的屬性property解析:

Property “屬性”(property)是Objective-C的一項特性,用於封裝物件中的資料。使用了屬性的話,編譯器就會自動編寫訪問這些屬性所需的方法(setter和getter),這個過程稱為“自動合成”(autosynthesis)。 @p

Objective C 學習心得 :--Windows下搭建objective C開發環境

      最近打算針對iPhone、iPod touch和iPad開發一些應用,所以,需要開始學習Objective C(蘋果推出的類似C語言的開發語言)。由於蘋果的自我封閉的產業鏈發展模式(從晶片、機器、開發語言、終端產品、服務)的限制,要想開發針對蘋果iPhone等產品

Kubernetes容器執行CRI簡介

Kubernetes節點的底層由一個叫做“容器執行時”的軟體進行支撐,它負責比如啟停容器這樣的事情。最廣為人知的容器執行時當屬Docker,但它不是唯一的。事實上,容器執行時這個領域發展迅速。為了使Kubernetes的擴充套件變得更容易,我們一直在打磨支援容器

C# 執行Thread的常見解決方案

前言:多執行緒程式設計是應用程式開發中一個非常重要的部分,這裡總結一些常見的執行緒(Thread)解決方案。注意:這裡僅涉及Thread的常見解決方案,CLR 4.0以後的Task這裡不做描述。目錄1、使用BackgroundWorker元件2、執行緒計時器3、執行緒池4、執

Objective-C學習筆記——OC實現最簡單的數學運算

      本篇帖子會實現使用OC的最簡單的加減乘除運算,學習的知識點包括變數定義,運算方法,格式化輸出等概念。主要學習基本的語法,其實和C語言的語法還是比較類似的。具體程式碼只要寫在main方法中就行了。詳細程式碼如下:#import <Foundation/Foun

Objective-C學習筆記十三——函式的宣告與呼叫

       OC中的函式語法也同樣與C類似。需要宣告,呼叫等等。具體實現看一下程式碼:(一)程式碼一:int area(int x,int y);//在此處宣告函式; int main(int argc, const char * argv[]) { @autor

Objective-C基礎筆記1基本概念和第一個程式

一、基本概念Objective-C(簡稱OC)是iOS開發的核心語言,蘋果公司在維護,在開發過程中也會配合著使用C語言、C++,OC主要負責UI介面,C語言、C++可用於圖形處理。C語言是面向過程的語言,OC是在C語言基礎上加上了一層面向物件的語法(將複雜面向物件語法去掉了)

Objective-C 執行程式設計指南 之 Type Encodings

為了幫助執行時系統,編譯器將每個方法的返回值型別和引數型別編碼成了字串,並把字串與方法選擇器關聯起來。 它使用的編碼方案在其他情況下也是有用的,因此該方案使用 @encode() 編譯器指令設定成了公共可用的。當給定一個型別說明, @encode() 會返回這個

Objective-C學習筆記——迴圈語句for和do-while的使用

      在OC中,除了while這種迴圈方式外,還有另外for迴圈和do-while迴圈,它們在不同的業務邏輯下會有不同的作用。可以和C語言和Java對比著學習。(一)程式碼一:int main(int argc, const char * argv[]) { @

Objective-C的協議Protocol——協議的實現

協議就是定義了一組方法,然後要求其他類去實現。 以下類的複製來舉例說明,遵守NSCopying協議的類是如何實現複製的。 0x01 NSCopying協議 NSCopying是物件拷貝的協議。 類的物件如果支援拷貝,則該類應遵守並實現NSCopying協議。 用

iOS學習筆記56Runtime-Objective-C Runtime 執行之三:方法與訊息

前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。 基礎資料型別 SEL

CRTC Runtime Library—— C/C++執行

C runtime library(part of the C standard library) 任何一個 C 程式,它的背後都有一套龐大的程式碼來進行支撐,使得該程式得以執行在更高級別上,而不必擔心同計算機底層操作的細節,這套程式碼至少包括:

[面試] C/C++ 語法—— RTTI執行型別資訊

RTTI(RunTime Type Information),顧名思義,物件執行時型別資訊,以便在執行時進行型別識別。 C++ 的物件識別可通過以下三個技術得以實現: (1)dynamic_cast 運算子(型別安全的向下轉型) 向下轉型是不安全的,多型

C執行C Run-time Library詳解

一、什麼是C執行時庫1)C執行時庫就是 C run-time library,是 C 而非 C++ 語言世界的概念:取這個名字就是因為你的 C 程式執行時需要這些庫中的函式.2)C 語言是所謂的“小核心”語言,就其語言本身來說很小(不多的關鍵字,程式流程控制,資料型別等);所以,C 語言核心開發出來之後,De