isa、superclass的细节
看下isa
存值的问题
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MJPerson : NSObject <NSCopying>
@end
@implementation MJPerson
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
@end
@interface MJStudent : MJPerson <NSCoding>
@end
@implementation MJStudent
- (void)encodeWithCoder:(NSCoder *)aCoder
{
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
return nil;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJStudent alloc] init];
Class personClass = [MJPerson class];
Class personMetaClass = object_getClass(personClass);
NSLog(@"%p-----%p------%p", person, personClass, personMetaClass);
}
return 0;
}
断点打印
断点打印打印得到person
的isa
的指向的值为0x001d8001000012f1
,打印的personClass
地址为0x00000001000012f0
。
好像不符合instance
对象的isa
指向class
对象,这是因为从64bit开始,isa需要进行一次位运算,才能计算出真实地址,可以从objc4
的源码找到
找到ISA_MASK
在X86_64(模拟器)
下值是0x00007ffffffffff8
通过person
的isa
与0x00007ffffffffff8
与运算得到0x00000001000012f0
,和personClass
打印的地址一致。
那superclass
的存值是否有这个问题
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MJPerson : NSObject <NSCopying>
@end
@implementation MJPerson
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
@end
@interface MJStudent : MJPerson <NSCoding>
@end
@implementation MJStudent
- (void)encodeWithCoder:(NSCoder *)aCoder
{
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
return nil;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
MJStudent *student = [[MJStudent alloc] init];
Class personClass = [MJPerson class];
Class studentClass = [MJStudent class];
Class personMetaClass = object_getClass(personClass);
Class studentMetaClass = object_getClass(studentClass);
NSLog(@"%p-----%p------%p", person, personClass, personMetaClass);
}
return 0;
}
断点打印
superclass打印直接不能打印studentClass
的superclass
指针,我们看看class
类的结构
自定义一个结构体mj_objc_class
,强制转换class
类
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MJPerson : NSObject <NSCopying>
@end
@implementation MJPerson
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
@end
@interface MJStudent : MJPerson <NSCoding>
@end
@implementation MJStudent
- (void)encodeWithCoder:(NSCoder *)aCoder
{
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
return nil;
}
@end
struct mj_objc_class {
Class isa;
Class superclass;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
MJStudent *student = [[MJStudent alloc] init];
struct mj_objc_class *personClass = (__bridge struct mj_objc_class *)([MJPerson class]);
struct mj_objc_class *studentClass = (__bridge struct mj_objc_class *)([MJStudent class]);
struct mj_objc_class *personMetaClass = (__bridge struct mj_objc_class *)(object_getClass([MJPerson class]));
struct mj_objc_class *studentMetaClass = (__bridge struct mj_objc_class *)(object_getClass([MJStudent class]));
NSLog(@"%p-----%p------%p", person, personClass, personMetaClass);
}
return 0;
}
断点打印
断点打印打印结果studentClass
的superclass
的值0x00000001000012f8
正好是personClass
的地址,studentMetaClass
的superclass
的值0x00000001000012d0
正好是personMetaClass
的地址,也就是superclass
的值就是父类的地址。
现在研究class
的结构
objc_class
结构体存放的信息如图
根据这个结构体写一个自己的类来强制转换class
类
#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 */
因为有 C++混编,main.m
改成main.mm
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "MJClassInfo.h"
// MJPerson
@interface MJPerson : NSObject <NSCopying>
{
@public
int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end
@implementation MJPerson
- (void)test
{
}
- (void)personInstanceMethod
{
}
+ (void)personClassMethod
{
}
- (id)copyWithZone:(NSZone *)zone
{
return nil;
}
@end
// MJStudent
@interface MJStudent : MJPerson <NSCoding>
{
@public
int _weight;
}
@property (nonatomic, assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end
@implementation MJStudent
- (void)test
{
}
- (void)studentInstanceMethod
{
}
+ (void)studentClassMethod
{
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
return nil;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJStudent *stu = [[MJStudent alloc] init];
stu->_weight = 10;
mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
class_rw_t *studentClassData = studentClass->data();
class_rw_t *personClassData = personClass->data();
class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
class_rw_t *personMetaClassData = personClass->metaClass()->data();
NSLog(@"1111");
}
return 0;
}
断点打印
superclass打印看到class
对象studentClass
的superclass
是MJPerson
的class
对象,personClass
的superclass
是NSObject
的class
对象。
打印看到对象方法和属性信息都是存放在class
对象中
打印看到类方法存放在meta-class
对象中
面试题
对象的isa指针指向哪里?
-
instance
对象的isa
指向class
对象 -
class
对象的isa
指向meta-class
对象 -
meta-class
对象的isa
指向基类的meta-class
对象
OC的类信息存放在哪里?
- 对象方法、属性、成员变量、协议信息,存放在
class
对象中 - 类方法,存放在
meta-class
对象中 - 成员变量的具体值,存放在
instance
对象