1. 程式人生 > 實用技巧 >IOS高階教程2:反射根據變數的引用獲取變數名

IOS高階教程2:反射根據變數的引用獲取變數名

一、使用介紹

專案有的時候,會遇到一些特殊的處理,想要根據一個例項的引用,獲取這個例項在程式碼中的名稱。比如在處理View的座標的時候,我們將UIView的座標資訊配置到plist檔案中,我們可以設定一個key,再通過這個key來獲取配置檔案中的座標等資訊。有沒有更簡單的方法呢,或者我只想簡單的用例項變數的變數名做為key。下面就介紹一種簡單的,根據例項變數的引用獲取例項變數名的辦法。

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

二、引用檔案

第一步,我們需要引入我們需要的標頭檔案,在需要使用的類中引用下面程式碼

#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)根據上面的原理我們可以得到第一版本的程式碼,如下:

- (NSString *)nameWithInstance:(id)instance
{
    unsigned int numIvars = 0;
    NSString *key=nil;
    Ivar * ivars = class_copyIvarList([self.target class], &numIvars);
    for(int i = 0; i < numIvars; i++) {
        Ivar thisIvar = ivars[i];
        if ((object_getIvar(self.target, thisIvar) == instance)) {
            key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
            break;
        }
    } 
    free(ivars);
    return key;

}

(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的時候,需要跳過執行。最終程式碼如下:

- (NSString *)nameWithInstance:(id)instance
{
    unsigned int numIvars = 0;
    NSString *key=nil;
    Ivar * ivars = class_copyIvarList([self.target class], &numIvars);
    for(int i = 0; i < numIvars; i++) {
        Ivar thisIvar = ivars[i];
        const char *type = ivar_getTypeEncoding(thisIvar);
        NSString *stringType =  [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
        if (![stringType hasPrefix:@"@"]) {
            continue;
        }
        if ((object_getIvar(self.target, thisIvar) == instance)) {
            key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
            break;
        }
    }
    free(ivars);
    return key;

}
來源:南安網站建設