Ivar
Ivar 是一種代表類中實例變量的類型。
typedef struct ivar_t *Ivar;
而 ivar_t在上面的成員變量列表中也提到過:
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
// alignment is sometimes -1; use alignment() instead
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
可以根據(jù)實例查找其在類中的名字,也就是“反射”:
-(NSString *)nameWithInstance:(id)instance {
unsigned int numIvars = 0;
NSString *key=nil;
Ivar * ivars = class_copyIvarList([self 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, thisIvar) == instance)) {//此處若 crash 不要慌!
key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
break;
}
}
free(ivars);
return key;
}
class_copyIvarList函數(shù)獲取的不僅有實例變量,還有屬性。但會在原本的屬性名前加上一個下劃線。
objc_property_t
@property標記了類中的屬性,這個不必多說大家都很熟悉,它是一個指向objc_property結(jié)構(gòu)體的指針:
typedef struct property_t *objc_property_t;
可以通過 class_copyPropertyList 和 protocol_copyPropertyList 方法來獲取類和協(xié)議中的屬性:
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
返回類型為指向指針的指針,哈哈,因為屬性列表是個數(shù)組,每個元素內(nèi)容都是一個 objc_property_t 指針,而這兩個函數(shù)返回的值是指向這個數(shù)組的指針。
舉個栗子,先聲明一個類:
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
你可以用下面的代碼獲取屬性列表:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
你可以用 property_getName函數(shù)來查找屬性名稱:
const char *property_getName(objc_property_t property)
你可以用class_getProperty 和 protocol_getProperty通過給出的名稱來在類和協(xié)議中獲取屬性的引用:
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
你可以用property_getAttributes函數(shù)來發(fā)掘?qū)傩缘拿Q和@encode類型字符串:
const char *property_getAttributes(objc_property_t property)
把上面的代碼放一起,你就能從一個類中獲取它的屬性啦:
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
對比下class_copyIvarList 函數(shù),使用 class_copyPropertyList 函數(shù)只能獲取類的屬性,而不包含成員變量。但此時獲取的屬性名是不帶下劃線的。
protocol_t
雖然 Objective-C的 Category 和 protocol 拓展能力有限,但也得為了將就 Swift 的感受,充個胖子。
flags 32 位指針最后兩位是給加載 Mach-O 的 fix-up 階段使用的,前 16 位預留給 Swift 用的。
protocol 主要內(nèi)容其實是(可選)方法,其次就是繼承其他 protocol。Swift 還支持 protocol 多繼承,所以需要 protocols 數(shù)組來做兼容。
struct protocol_t : objc_object {
const char *mangledName;
struct protocol_list_t *protocols;
method_list_t *instanceMethods;
method_list_t *classMethods;
method_list_t *optionalInstanceMethods;
method_list_t *optionalClassMethods;
property_list_t *instanceProperties;
uint32_t size; // sizeof(protocol_t)
uint32_t flags;
// Fields below this point are not always present on disk.
const char **_extendedMethodTypes;
const char *_demangledName;
property_list_t *_classProperties;
... 省略一些封裝的便捷 get 方法
}
IMP
IMP在objc.h中的定義是:
typedef void (*IMP)(void /* id, SEL, ... */ );
它就是一個函數(shù)指針,這是由編譯器生成的。當你發(fā)起一個 ObjC 消息之后,最終它會執(zhí)行的那段代碼,就是由這個函數(shù)指針指定的。而 IMP這個函數(shù)指針就指向了這個方法的實現(xiàn)。既然得到了執(zhí)行某個實例某個方法的入口,我們就可以繞開消息傳遞階段,直接執(zhí)行方法,這在后面會提到。
你會發(fā)現(xiàn) IMP 指向的方法與 objc_msgSend 函數(shù)類型相同,參數(shù)都包含 id 和 SEL 類型。每個方法名都對應一個 SEL 類型的方法選擇器,而每個實例對象中的 SEL 對應的方法實現(xiàn)肯定是唯一的,通過一組 id 和 SEL 參數(shù)就能確定唯一的方法實現(xiàn)地址;反之亦然。