OC 对象有三种
-
instance
实例对象 -
class
类对象 -
meta-class
元类
实例对象
通过alloc
出来的对象都是实例对象,而且由于它们都继承自NSObject
所以他们都有一个class isa
的成员变量.那么实例对象在内存中存储的信息就是:
-
isa
指针 - 其他成员变量
类对象
image.png一个类对象在内存中只有一份,存储信息:
-
isa
指针 -
superclass
指针 - 类的属性信息(
@property
) - 类的对象方法信息(
instance
方法) - 类的协议信息(
protocol
) - 类的成员变量信息(
ivar
)
元类
image.png注意:这里只能用
runtime
的方式获取元类,而调用class
方法不论多少次都是只返回类对象.都是Class
类型结构一样.每个类在内存有且只有一个元类对象,存储信息主要包括
-
isa
指针 -
superclass
指针 -
类的类方法
(class method
)
先来看一张图:
instance
的isa
指向class
- 当调用对象方法的时候,通过instance
isa
找到class
,最后找到对象方法进行实现.
class
类对象的isa指向元类, - 当调用类方法的时候,通过类对象的
isa
找到元类,最后找到类方法进行实现.
superclass 是在调用父类的方法用到的.
为什么说instance
的isa
指向class
的呢?下面来简单验证下:
@interface father :NSObject
@end
@implementation father
@end
@interface son : father
@end
@implementation son
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
son *sonInstacne = [[son alloc]init];
Class sonClass = [son class];
Class metaClass = object_getClass(sonClass);
NSLog(@"%p --%p---%p",sonInstacne,sonClass,metaClass);
return 0;
}
}
断点调试:这里注意一下64位环境下isa要通过& ISAMASK
运算才能算出真正的地址:
ISAMASK
是个宏,通过runtime源码发现:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8;
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
};
image.png
这里可看出sonInstacne的isa是指向 sonclass的.接下来分析sonclass的isa是否指向metaclass:
image.png
备注:p/x print 输出 x 输出十六进制.po = print object
再来看网上的一张图:
image.png
上图分析可知:
- 实例对象
instance
的isa
是指向类对象class
- 类对象的
isa
是指向元类对象meta-class
- 元类对象的
isa
是指向基类root class
- 类对象的
superclass
是指向父类的class
- 基类的元类
superclass
是指向基类的class(这个比较特殊)
小结:
实例方法调用流程: -
isa
找到class
,方法不存在,通过superclass
找到父类,最后到基类的class
也找到不到,抛出异常.
类对象方法调用流程: -
isa
找meta-class
,方法不存在,通过superclass
找,最后基类的元类会去其类对象中找.(下面举例说明)
新建一个person类,.m文件不实现该方法.
@interface Person : NSObject
+ (void)test;
@end
新建NSObject分类
@interface NSObject (test)
+ (void)test;
@end
.m文件实现用实例方法
@implementation NSObject (test)
//+ (void)test{
// NSLog(@"NSObject-test---%p",self);
//}
- (void)test{
NSLog(@"这是实例方法");
}
@end
image.png
person类对象通过isa找到元类对象,在里面没有发现方法,前去superclass找,基类的元类对象也没找到,就去该基类的类对象找,(而这里是存放实例方法的).为什么会这样?
这里涉及到runtime相关,先知道是这么回事就行.后面在补充runtime.
补充:Class 结构分析
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; // 用于获取具体的类信息
}
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
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 firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;//占用空间
#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;
}
};
面试题:
1,对象的isa指向哪里?
- 实例对象的isa指向class,class的isa指向meta-class,元类的isa指向基类的meta-class.
2,类信息存放在哪里?
- 对象方法,属性,成员,协议存放在class里面,
- 类方法存放在元类里面,
- 成员变量的具体值是存放在instance里面
网友评论