Objective-C Runtime:深刻理解成員變量與屬性

類型編碼 在Runtime中,編譯器將每一個方法的返回值和參數類型編碼爲一個字符串,並將其與方法的selector關聯在一塊兒。面試

因爲該編碼方案具備必定的通用性,系統提供了編譯器指令@encode來獲取特定編碼後的字符串。數組

當給定一個類型時,@encode返回這個類型的字符串編碼。這些類型能夠是諸如int、指針等基本類型,也能夠是結構體、類等類型。安全

事實上,任何能夠做爲sizeof()操做參數的類型均可以執行@encode()指令。網絡

在Objective-C Runtime Programming Guide中的Type Encoding一節中,列出了Objective-C中全部的類型編碼。須要注意的是這些類型不少是與咱們用於存檔和分發的編碼類型是相同的。但有一些不能在存檔時使用,以下所示:數據結構

注意:Objective-C不支持long double類型。@encode(long double)返回d,與double是同樣的。 針對數組的類型編碼,返回字符串會包括:數組元素的個數以及元素的類型,具體以下所示:ide

int a[] = {1, 2}; NSLog(@"type Coding = %s", @encode(typeof(a))); 打印結果以下:函數

2018-03-28 22:46:28.253495+0800 RuntimeUsage[48760:1909814] type Coding = [2i] 對於屬性而言,還會有一些特殊的類型編碼,以代表屬性是隻讀、拷貝、retain等等,詳情能夠參考Property Type String。ui

成員變量與屬性 成員變量與屬性這一部分有三個方面須要注意:Ivar、objc_property_t基本數據結構和關聯對象(Associated Object)。其中,關於關聯對象的相關內容在以前的文章中詳細闡述過。編碼

基礎數據結構 成員變量(Ivar)的數據結構atom

在Objective-C中,成員變量即Ivar類型,是指向結構體struct objc_ivar的指針,在Objc/runtime.h 中查到,以下所示:

typedef struct objc_ivar *Ivar; 結構體struct objc_ivar的數據結構以下所示:

struct objc_ivar { char *ivar_name OBJC2_UNAVAILABLE; // 變量名。 char *ivar_type OBJC2_UNAVAILABLE; // 變量類型。 int ivar_offset OBJC2_UNAVAILABLE; // 基地址偏移量,在對成員變量尋址時使用。 #ifdef LP64 int space OBJC2_UNAVAILABLE; #endif } 屬性的數據結構 屬性(property)數據結構以下所示:

typedef struct objc_property *objc_property_t; 屬性特性(Attribute)的數據結構以下所示:

typedef struct { const char * _Nonnull name; /< The name of the attribute */ const char * _Nonnull value; /< The value of the attribute (usually empty) */ } objc_property_attribute_t; 成員變量與屬性的聯繫 本質上,一個屬性必定對應一個成員變量,可是屬性又不只僅是一個成員變量,屬性還會根據本身對應的屬性特性的定義來對這個成員變量進行一系列的封裝:提供 Getter/Setter 方法、內存管理策略、線程安全機制等等。

成員變量、屬性的操做方法

成員變量

成員變量的相關函數以下:

// 獲取成員變量名 const char * ivar_getName ( Ivar v ); // 獲取成員變量類型編碼 const char * ivar_getTypeEncoding ( Ivar v ); // 獲取成員變量的偏移量 ptrdiff_t ivar_getOffset ( Ivar v ); ivar_getOffset函數,對於類型id或其它對象類型的實例變量,能夠調用object_getIvar和object_setIvar來直接訪問成員變量,而不使用偏移量。

關聯對象 關聯對象函數以下:

// 設置關聯對象 void objc_setAssociatedObject ( id object, const void *key, id value, objc_AssociationPolicy policy ); // 獲取關聯對象 id objc_getAssociatedObject ( id object, const void *key ); // 移除關聯對象 void objc_removeAssociatedObjects ( id object ); 屬性 屬性相關函數以下:

// 獲取屬性名 const char * property_getName ( objc_property_t property ); // 獲取屬性特性描述字符串 const char * property_getAttributes ( objc_property_t property ); // 獲取屬性中指定的特性 char * property_copyAttributeValue ( objc_property_t property, const char *attributeName ); // 獲取屬性的特性列表 objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned int *outCount ); property_copyAttributeValue函數,返回的char *在使用完後須要調用free()釋放。

property_copyAttributeList函數,返回值在使用完後須要調用free()釋放。

運行時操做成員變量和屬性的示例代碼 NSString * runtimePropertyGetterIMP(id self, SEL _cmd){ Ivar ivar = class_getInstanceVariable([self class], "_runtimeProperty");

return object_getIvar(self, ivar);

}

void runtimePropertySetterIMP(id self, SEL _cmd, NSString *value){ Ivar ivar = class_getInstanceVariable([self class], "_runtimeProperty"); NSString *aValue = (NSString *)object_getIvar(self, ivar); if (![aValue isEqualToString:value]) { object_setIvar(self, ivar, value); } }

  • (void)verifyPropertyAndIvar{

#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector"

//一、Add property and getter/setter method
Class cls = objc_allocateClassPair([Animal class], "Panda", 0);
 
//add instance variable
BOOL isSuccess = class_addIvar(cls, "_runtimeProperty", sizeof(cls), log2(sizeof(cls)), @encode(NSString));
NSLog(@"%@", isSuccess ? @"成功" : @"失敗");//print 成功
 
//add attributes
objc_property_attribute_t type = {"T", "@\"NSString\""};
objc_property_attribute_t ownership = {"C", ""};//C = Copy
objc_property_attribute_t isAutomic = {"N", ""};// N = nonatomic
objc_property_attribute_t backingVar = {"V", "_runtimeProperty"};
objc_property_attribute_t attrubutes[] = {type, ownership, isAutomic, backingVar};
class_addProperty(cls, "runtimeProperty", attrubutes, 4);
class_addMethod(cls, @selector(runtimeProperty), (IMP)runtimePropertyGetterIMP, "@@:");
class_addMethod(cls, @selector(setRuntimeProperty), (IMP)runtimePropertySetterIMP, "V@:");
 
objc_registerClassPair(cls);
 
//二、print all properties
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList(cls, &count);
for (int32_t i = 0; i < count; i ++) {
    objc_property_t property = properties[I];
    NSLog(@"%s, %s\n", property_getName(property), property_getAttributes(property));
    //print: _runtimeProperty, T@"NSString",C,N,V_runtimeProperty
     
}
free(properties);
 
//三、print all Ivar
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int32_t i = 0; i < outCount; i ++) {
    Ivar ivar = ivars[I];
    NSLog(@"%s, %s\n", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
    //print:_runtimeProperty, {NSString=#}
    
}
free(ivars);
 
//四、use property
id panda = [[cls alloc] init];
[panda performSelector:@selector(setRuntimeProperty) withObject:@"set-property"];
NSString *propertyValue = [panda performSelector:@selector(runtimeProperty)];
NSLog(@"return value = %@", propertyValue);
//print: return value = set-property
 
//五、destory
panda = nil;
objc_disposeClassPair(cls);

#pragma clang diagnostic pop

} 上述代碼打印信息:

成功 runtimeProperty, T@"NSString",C,N,V_runtimeProperty _runtimeProperty, {NSString=#} return value = set-property 上面的代碼中,咱們在運行時動態建立了Animal 的一個子類 Panda;

而後爲它動態添加了 Ivar:_runtimeProperty、對應的 Property:runtimeProperty、對應的 Getter/Setter方法:runtimeProperty``setRuntimeProperty;

接着咱們遍歷和打印了Panda 的 Ivar 列表和 Property 列表;

而後建立了 Panda 的一個實例 panda,並使用了 Property;

最後咱們銷燬了 panda 和 Panda。

這裏有幾點須要注意的:

咱們不能用 class_addIvar() 函數爲一個已經存在的類添加Ivar,而且 class_addIvar() 只能在 objc_allocateClassPair() 和 objc_registerClassPair() 之間調用;

添加屬性特性時的各類類型字符能夠參考:Property Type String。

添加一個屬性及對應的成員變量後,咱們還能經過 [obj valueForKey:@"propertyName"];得到屬性值。

小結 本文主要講解了成員變量與屬性相關使用,尤爲是關聯對象的使用。但願閱讀完本文,能對成員變量和屬性的理解更深刻。

文章來源於網絡 若有侵權請及時聯繫本人刪除 小編這裏推薦一個羣:691040931 裏面有大量的書籍和麪試資料,不少的iOS開發者都在裏面交流技術

相關文章
相關標籤/搜索