文章爲原創,轉載請註明出處
內省(instrospection)
- 在運行期檢視對象類型這一操作也叫“類型消息查詢”。
對象只能分配在堆上 & id本身也是指針
NSString *string = @"Some String"; // 正確
NSSSring string = @"Some String"; // 錯誤
id string = @"Some String"; // 正確
對象&類對象
- 每個對象的首個指針是Class類型的指針"isa",指向自己的類對象
typedef struct objc_object {
Class isa;
} id;
- 這個結構體存儲類的元數據,例如類有哪些實例變量,實現了哪些方法
- 這個結構體的首個指針也是"isa"指針,說明類結構Class本身也爲Objectice-C對象
- 存儲了super_class,isa指針指向類對象的元類對象,元類對象的isa指針指向父類的元類對象一直指向根類的元類對象一般爲NSObject,根類元類對象的isa指針指向自己,它super_class指針指根類自己,根類的super_class爲nil
typedef struct objc_class *Class;
struct objc_class {
Class isa;
Class super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodList;
struct objc_cache *cache;
struct objc_protocol_list *protocols;
};
可變 & 不可變
- 對象的內存排布可以看作一個結構體,結構體的大小不能變化,所以運行時不能動態的給對象增加成員變量
- 對象方法保存在可變區域中,可以看到objc_method_list是一個指向指針的指針,所以可以通過修改改指針指向的指針的值來動態的添加方法,這也就是Category只能添加方法,不能添加實例變量的原因
- objc_setAssociatedObject 和 objc_getAssociatedObject雖然可以動態添加成員變量,但實現機制不同,並不是真的改變了對象的內存結果
isKindOfClass & isMemberOfClass
- isKindOfClass:判斷類對象是否是某個類或其派生類的實例
- isMemberOfClass:判斷對象是否爲某個特定類的實例
NSMutableDictionary *dict = [NSMutableDictionary class];
[dict isMemberOfClass: [NSDictionary class]]; // NO
[dict isMemberOfClass: [NSMutableDictionary class]; // YES
[dict isKindOfCalss: [NSDictionary class]; // YES
[dict isKindOfClass: [NSArray class]]; // NO
像這樣的類型查詢方法使用"isa"指針來獲取對象所屬的類,用super_class在繼承體系中游走
對象的等同性
“==” & isEqual
- ==:判斷兩個指針本身,而不是它們指向的對象
- isEqual:比較是否爲同一個對象
- isEqualToString:NSString自己的類型判斷方法,傳遞對象必須是NSString,因爲不用判斷類型,所以快
NSString *foo = @"Badger";
NSString *bar = [NSStringstringWith Format: @"Barger"];
BOOL equalA = foo == bar; // NO
BOOL euqalB = [foo isEqual: bar]; // YES
BOOL equalC = [foo isEqualToString: bar]; // YES
isEqual: & Hash
- NSObject 對這兩個方法的默認實現是,當且僅當指針值(內存地址)完全相等時對象才相等
- isEqual:方法判定兩個對象相等,那麼其Hash必須返回同樣的值,但Hash返回同樣的值,isEqual不一定相等
- isEqual: 的簡單實現
- (BOOL)isEqual: (id)object {
if (self == object) return YES;
if ([self class] != [object class]) retrun NO;
LNPerson *otherPerson = (LNPerson *)object;
if (![_lastName isEqualToString: otherPerson.lastName]
return NO;
if (_age != otherPerson.age)
return NO;
return YES;
}
- Hash 的簡單實現
要注意使用的值的hashValue最好不要是可變類型,不然在Set這樣的數據結構裏會出現問題
Hash計算的原則是,計算快的,碰撞概率低的算法
{
return [firstValue hash] ^ [secondValue hash];
}
isEqualToString:類似方法的使用原因
- 使用類不相符的時候會警告,讓代碼看起來更美觀,易讀,比isEqual更快,常用方法是,如果受測參數與該接受消息的對象屬於同一個類就調用自己的判斷方法,否則交給超類處理
等同判斷的執行深度
- 有時候不需要判斷一個對象的所有值都相等,比如當一個對象有ID時,僅判斷其ID即可