Objective C執行時(runtime)
前言:
Objective C的runtime技術功能非常強大,能夠在執行時獲取並修改類的各種資訊,包括獲取方法列表、屬性列表、變數列表,修改方法、屬性,增加方法,屬性等等,本文對相關的幾個要點做了一個小結。
目錄:
(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 |
程式碼示例一:
1 @implementation CA
3 void dynamicMethodIMP( id self , SEL _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 self , SEL _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學習筆記56(Runtime)-Objective-C Runtime 執行時之三:方法與訊息
前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。 基礎資料型別 SEL
CRT(C 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