文章为原创,转载请注明出处
内省(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即可