反射根據變量的引用獲取變量名

一、使用介紹

    項目有的時候,會遇到一些特殊的處理,想要根據一個實例的引用,獲取這個實例在代碼中的名稱。比如在處理View的座標的時候,我們將UIView的座標信息配置到plist文件中,我們可以設置一個key,再通過這個key來獲取配置文件中的座標等信息。有沒有更簡單的方法呢,或者我只想簡單的用實例變量的變量名做爲key。下面就介紹一種簡單的,根據實例變量的引用獲取實例變量名的辦法。

轉載請保留原本鏈接:http://my.oschina.net/taptale/blog/110626

二、引用文件

    第一步,我們需要引入我們需要的頭文件,在需要使用的類中引用下面代碼

1 #import <objc/runtime.h>

三、運行原理

    我們可以從蘋果官方的開發文檔中查看到詳細的運行時的使用方法及API,官方並沒有直接提供根據實例的引用獲取實例變量名稱的辦法,所以我們需要自己去實現。

 在官方的API中我們可以找到以下幾個方法

    (1)Describes the instance variables declared by a class.    

Ivar * class_copyIvarList(Class cls, unsigned int *outCount)        
      (2) Reads the value of an instance variable in an object.

 id object_getIvar(id object, Ivar ivar)

     (3) Returns the name of an instance variable.

const char * ivar_getName(Ivar ivar)
根據以上的API,我們可以根據變量的擁有者獲取所有變量的Ivar,再迭代所有Ivar,每一次迭代做如下操作
  • 根據(2)中的API,我們可以獲取到當前迭代中的Ivar對應的實例變量的引用
  • 將獲取到的實例變量與傳遞過來的實例變量的地址比較
  • 如果地址相同,說明當前的Ivar爲傳遞過來實例變量的Ivar,可以通過(3)獲取變量的名稱並返回

四、代碼

(1)根據上面的原理我們可以得到第一版本的代碼,如下:

01 - (NSString *)nameWithInstance:(id)instance
02 {
03     unsigned int numIvars = 0;
04     NSString *key=nil;
05     Ivar * ivars = class_copyIvarList([self.target class], &numIvars);
06     for(int i = 0; i < numIvars; i++) {
07         Ivar thisIvar = ivars[i];
08         if ((object_getIvar(self.target, thisIvar) == instance)) {
09             key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
10             break;
11         }
12     }
13     free(ivars);
14     return key;
15  
16 }

(2)在測試中發現到達上面的if語句的時候,程序有的時候就會crash,經詳細測試發現,每次迭代到非objective-c對象的時候,如基本數據類型,BOOL、int、float就會報錯。

原因出在object_getIvar這個方法中,當遇到非objective-c對象時,並直接crash,後來查看官方解釋

The value of the instance variable specified by ivar, or nil if object is nil.
並沒有明確的給出遇到非對象時會crash,也並不會返回nil

我們需要進行一下修正,當遇到非objective-c的時候,需要跳過執行。最終代碼如下:

01 - (NSString *)nameWithInstance:(id)instance
02 {
03     unsigned int numIvars = 0;
04     NSString *key=nil;
05     Ivar * ivars = class_copyIvarList([self.target class], &numIvars);
06     for(int i = 0; i < numIvars; i++) {
07         Ivar thisIvar = ivars[i];
08         const char *type = ivar_getTypeEncoding(thisIvar);
09         NSString *stringType =  [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
10         if (![stringType hasPrefix:@"@"]) {
11             continue;
12         }
13         if ((object_getIvar(self.target, thisIvar) == instance)) {
14             key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
15             break;
16         }
17     }
18     free(ivars);
19     return key;
20  
21 }

原文鏈接:http://my.oschina.net/taptale/blog/110626
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章