標籤:
一、使用介紹
項目有的時候,會遇到一些特殊的處理,想要根據一個執行個體的引用,擷取這個執行個體在代碼中的名稱。比如在處理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)根據上面的原理我們可以得到第一版本的代碼,如下:
?
| 12345678910111213141516 |
- (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對象的時候,如基礎資料型別 (Elementary Data Type),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的時候,需要跳過執行。最終代碼如下:
?
| 123456789101112131415161718192021 |
- (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; }
|
IOS進階教程2:反射根據變數的引用擷取變數名