OC对象的分类
-
instance 对象(实例对象):通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
Student *p1 = [[Student alloc] init]; Student *p2 = [[Student alloc] init];instance对象在内存中存储的信息包括:
- isa指针
- 其他成员变量
-
class 对象(类对象):每个类在内存中有且只有一个class对象
NSObject *obj1 = [[NSObject alloc] init]; Class objClass1 = [obj1 class]; Class objClass2 = [NSObject class]; //class 方法返回的一直是class对象,类对象,而不是元类对象 Class objClass3 = [[NSObject class] class]; Class objClass4 = object_getClass(obj1); NSLog(@"%p-%p-%p-%p",objClass1,objClass2,objClass3,objClass4);class对象在内存中存储的信息主要包括:
- isa指针
- superclass指针
- 类的属性信息(@property)
- 类的对象方法信息(instance method)
- 类的协议信息(protocol)
- 类的成员变量信息(ivar):存储的成员变量的类型,名字等,相当于存储的描述信息,只需要存储一份
- ......
-
meta-class 对象(元类对象):每个类在内存中有且只有一个meta-class对象
Class metaClass = object_getClass([NSObject class]);meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括:
- isa指针
- superclass指针
- 类的类方法信息(class method)
- ......
class、objc_getClass、object_getClass 的区别
1.Class objc_getClass(const char *aClassName)
1> 传入字符串类名
2> 返回对应的类对象
2.Class object_getClass(id obj)
1> 传入的obj可能是instance对象、class对象、meta-class对象
2> 返回值
a) 如果是instance对象,返回class对象
b) 如果是class对象,返回meta-class对象
c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象
3.- (Class)class、+ (Class)class
1> 返回的就是类对象
- (Class) {
return self->isa;
}
+ (Class) {
return self;
}
isa和superclass
isa指针
- instance 的 isa 指向 class,当调用对象方法时,通过 instance 的 isa 找到 class,最后找到对象方法的实现进行调用
- class 的 isa 指向 meta-class,当调用类方法时,通过 class 的 isa 找到 meta-class,最后找到类方法的实现进行调用
类对象的superclass指针
当 Student 的 instance 对象要调用 Person 的对象方法时,会先通过 isa 找到 Student 的 class,然后通过 superclass 找到 Person 的 class,最后找到对象方法的实现进行调用
meta-class对象的superclass指针
当 Student 的 class 要调用 Person 的类方法时,会先通过 isa 找到 Student 的 meta-class,然后通过 superclass 找到 Person 的 meta-class,最后找到类方法的实现进行调用
isa、superclass总结
-
Instance 的 isa 指向 Class
-
Class 的 isa 指向 Meta-Class
-
Meta-class 的 isa 指向基类的 Meta-Class
-
Class 的 Superclass 指向父类的 Class,如果没有父类,Superclass指针为
nil -
Meta-Class 的 Superclass 指向父类的 Meta-Class,基类的 Meta-Class 的 Superclass 指向基类的 Class
-
Instance 调用实例方法的轨迹,isa 找到 Class,方法不存在,就通过 Superclass 找父类
-
Class 调用类方法的轨迹;isa 找 Meta-Class,方法不存在,就通过 Superclass 找父类
面试题
1.对象的isa指针指向哪里?
- instance对象的isa指向class对象
- class对象的isa指向meta-class对象
- meta-class对象的isa指向基类的meta-class对象
2.OC的类信息存放在哪里?
- 对象方法、属性、成员变量、协议信息,存放在class对象中
- 类方法,存放在meta-class对象中
- 成员变量的具体值,存放在instance对象
3.如果调用的类方法没有实现,是否会调用同名的实例方法?
会调用基类中同名的实例方法。
- Person 继承自 NSObject,都有
+test()类方法
#Person文件
@interface MJPerson : NSObject
+ (void)test;
@end
@implementation MJPerson
+ (void)test {
NSLog(@"+[MJPerson test] - %p", self);
}
@end
#NSObject+Test文件
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
+ (void)test {
NSLog(@"+[NSObject test] - %p", self);
}
@end
#调用方式:
NSLog(@"[MJPerson class] - %p", [MJPerson class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
[MJPerson test];
[NSObject test];
结果输出:
[MJPerson class] - 0x100001260
[NSObject class] - 0x7fff925be118
+[MJPerson test] - 0x100001260
+[NSObject test] - 0x7fff925be118
- 如果将 MJPerson 的类方法的实现注释掉,其他代码不变,结果输出:
[MJPerson class] - 0x100001220
[NSObject class] - 0x7fff925be118
+[NSObject test] - 0x100001220
+[NSObject test] - 0x7fff925be118
因为 MJPerson 里找不到 test 实现方法,即找父类的实现方法。
- 如果将 MJPerson 和 NSObject 的类方法的实现都注释掉,结果输出:
[MJPerson class] - 0x1000011a0
[NSObject class] - 0x7fff925be118
+[MJPerson test]: unrecognized selector sent to class 0x1000011a0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[MJPerson test]: unrecognized selector sent to class 0x1000011a0'
- 如果将 MJPerson 和 NSObject 的类方法的实现都注释掉,给 MJPerson 添加同名对象方法
#Person文件
@interface MJPerson : NSObject
+ (void)test;
@end
@implementation MJPerson
+ (void)test {
NSLog(@"+[MJPerson test] - %p", self);
}
- (void)test {
NSLog(@"-[MJPerson test] - %p", self);
}
@end
#NSObject+Test文件
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
+ (void)test {
NSLog(@"+[NSObject test] - %p", self);
}
@end
#调用方式:
NSLog(@"[MJPerson class] - %p", [MJPerson class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
[MJPerson test];
[NSObject test];
结果输出:
[MJPerson class] - 0x1000011e0
[NSObject class] - 0x7fff925be118
+[MJPerson test]: unrecognized selector sent to class 0x1000011e0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[MJPerson test]: unrecognized selector sent to class 0x1000011e0'
- 如果将 MJPerson 和 NSObject 的类方法的实现都注释掉,给 NSObject 添加同名对象方法
#Person文件
@interface MJPerson : NSObject
+ (void)test;
@end
@implementation MJPerson
+ (void)test {
NSLog(@"+[MJPerson test] - %p", self);
}
@end
#NSObject+Test文件
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
+ (void)test {
NSLog(@"+[NSObject test] - %p", self);
}
- (void)test {
NSLog(@"-[NSObject test] - %p", self);
}
@end
#调用方式:
NSLog(@"[MJPerson class] - %p", [MJPerson class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
[MJPerson test];
[NSObject test];
结果输出:
[MJPerson class] - 0x100001220
[NSObject class] - 0x7fff925be118
-[NSObject test] - 0x100001220
-[NSObject test] - 0x7fff925be118
由此可见,当类方法无实现时,会调用基类中同名的对象方法,上述辩证方法验证了 isa、superclass 指向图中的 Meta-Root Class 的 Superclass 指向 Root class(上图实线转弯处)。
调用方法实质是 Runtime 的消息转发机制,Runtime只会根据方法名寻找而不会在意是类方法还是实例方法。
4.讨论isa指针值
struct mj_objc_class {
Class isa;
};
MJPerson *person = [[MJPerson alloc] init];
Class personClass = [MJPerson class];
struct mj_objc_class *personClass2 = (__bridge struct mj_objc_class *)(personClass);
Class personMetaClass = object_getClass(personClass);
NSLog(@"%p %p %p", person, personClass, personMetaClass);
通过 LLDB 指令来查看 isa 值:
Printing description of person->isa:
(Class) isa = MJPerson
(lldb) p person->isa // 获取不到真正的isa指针
(Class) $0 = MJPerson
(lldb) p (long)person->isa // 10进制
(long) $1 = 8303516107936977
(lldb) p/x (long)person->isa // 16进制,实例对象isa指针值
(long) $2 = 0x001d8001000014d1
(lldb) p/x personClass // 类对象地址
(Class) $3 = 0x00000001000014d0 MJPerson
(lldb) p/x 0x001d8001000014d1 & 0x0000000ffffffff8 // 进行位运算
(long) $4 = 0x00000001000014d0
(lldb) p/x personClass->isa
error: member reference base type 'Class' is not a structure or union
// 类并没有暴露出isa指针值,定义了一个和类相似的结构体mj_objc_class,强制转换后,才获取到类对象的isa指针
(lldb) p/x personClass2->isa
(Class) $0 = 0x000001a10402d2e9 // 类对象的isa指针值
(lldb) p/x personMetaClass
(Class) $1 = 0x000000010402d2e8 // 元类对象地址
(lldb) p/x 0x000001a10402d2e9 & 0x0000000ffffffff8 // 进行位运算
(long) $2 = 0x000000010402d2e8
总结:
-
从 64bit 开始,isa 需要进行一次位运算,才能计算出该 isa 指针指向的真实地址。
-
isa指针需要
&ISA_MASK 值才能得出最终指向的真实地址 -
Runtime 中的 ISA_MASK :
# if __arm64__//iPhone # define ISA_MASK 0x0000000ffffffff8ULL # elif __x86_64__//mac # define ISA_MASK 0x00007ffffffffff8ULL
5.讨论superclass指针值,是否会像isa指针值一样?
Student 继承自 Person,自定义结构体来强制转换类
struct mj_objc_class {
Class isa;
Class superclass;
};
struct mj_objc_class *personClass = (__bridge struct mj_objc_class *)([MJPerson class]);
struct mj_objc_class *studentClass = (__bridge struct mj_objc_class *)([MJStudent class]);
NSLog(@"1111");
LLDB 指令调试:
(lldb) p/x studentClass->superclass
(Class) $0 = 0x00000001000014b8 MJPerson
(lldb) p/x personClass
(mj_objc_class *) $1 = 0x00000001000014b8
由此可见,studentClass 的 superclass 的值与 personClass 的地址相同
6.类的本质结构
Runtime 中 objc_class 的源码
# objc_object
struct objc_object {
private:
isa_t isa;// 类的isa指针是私有的
public:
// 诸多方法
}
# objc_class:objc_object
struct objc_class : objc_object {
// Class ISA;
Class superclass;
// 方法缓存
cache_t cache; // formerly cache pointer and vtable
// 用于获取具体类信息
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
// 诸多方法
}
# class_rw_t
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;// 只读类表
method_array_t methods;// 方法列表
property_array_t properties;// 属性列表
protocol_array_t protocols;// 协议列表
// 诸多方法
}
# class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;// instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;// 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;// 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
窥探 Struct objc_class 的结构
想LLDB命令查看相关类结构,可以采取仿写类结构进行打印查看,MJ大神的总结:
#import <Foundation/Foundation.h>
#ifndef MJClassInfo_h
#define MJClassInfo_h
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
};
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
};
struct method_t {
SEL name;
const char *types;
IMP imp;
};
struct method_list_t : entsize_list_tt {
method_t first;
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
};
struct ivar_list_t : entsize_list_tt {
ivar_t first;
};
struct property_t {
const char *name;
const char *attributes;
};
struct property_list_t : entsize_list_tt {
property_t first;
};
struct chained_property_list {
chained_property_list *next;
uint32_t count;
property_t list[0];
};
typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
uintptr_t count;
protocol_ref_t list[0];
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_list_t * methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t * protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
#define FAST_DATA_MASK 0x00007ffffffffff8UL
struct class_data_bits_t {
uintptr_t bits;
public:
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
};
/* OC对象 */
struct mj_objc_object {
void *isa;
};
/* 类对象 */
struct mj_objc_class : mj_objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
mj_objc_class* metaClass() {
return (mj_objc_class *)((long long)isa & ISA_MASK);
}
};
#endif /* MJClassInfo_h */