1. 程式人生 > >Objective C執行時(runtime)技術的幾個要點總結

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

前言:

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

目錄:

  (6) 總結

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

      使用該函式可以在執行時動態替換某個類的函式實現,這樣做有什麼用呢?最起碼,可以實現類似windowshook效果,即截獲系統類的某個例項函式,然後塞一些自己的東西進去,比如打個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         return@"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 @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

示例程式碼二:

@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。

示例程式碼三:

@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)在執行時對函式進行動態替換 : cl

Objective C執行runtime

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

執行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 語言所用到

Java 執行Thread技術與深入理解

Thread基礎部分 在各種程式語言中都有thread(執行緒)技術,執行緒保證在一個main中(主執行緒)可以同時進行兩個或多個不同的事件,通俗點說就是你在上廁所的同時還可以玩手機,是吧!美滋滋。而Thread就相當於提供了同時做兩件事的條件和環境。接下來在深入一點理解計算機中的Threa

可信執行環境TEE技術介紹

轉自:https://blog.csdn.net/trustbo/article/details/78234373 1. 當前移動安全背景   當前移動終端面臨這嚴重的安全威脅,威脅點如下圖所示:   因此移動廠商、使用者、服務提供商等各方都對移動安全提

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協議。 用

C++設計技巧之兩類互相擁有對方的物件指標

1、在實際的運用中我們也會用到如下的類關係: class B; class A { int i; B *lpb; } class B { int i; A* lpa; } 注意:一般來說,兩者的定義,至少有一方是使用指標,或兩者都使用指標,但是決不能兩者都定義實體物件。 這樣的實

C#程式設計學習04:基本操作學習總結

一、對話方塊窗體的設計 (1)修改exe圖示:專案-->右鍵-->屬性-->應用程式-->圖示和清單-->圖示,選擇要新增的圖示 (2)修改對話方塊圖示: 點選對話方塊 --> 屬性 --> ICON (3)固定對話方塊大小:點選對話方塊