isa、superclass的细节

2018-09-11  本文已影响0人  SKY_Tang

看下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;
}

断点打印

断点打印

打印得到personisa的指向的值为0x001d8001000012f1,打印的personClass地址为0x00000001000012f0

好像不符合instance对象的isa指向class对象,这是因为从64bit开始,isa需要进行一次位运算,才能计算出真实地址,可以从objc4的源码找到

ISA_MASK

找到ISA_MASKX86_64(模拟器)下值是0x00007ffffffffff8

ISA_MASK

通过personisa0x00007ffffffffff8与运算得到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打印

直接不能打印studentClasssuperclass指针,我们看看class类的结构

objc4源码 objc源码

自定义一个结构体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;
}

断点打印

断点打印

打印结果studentClasssuperclass的值0x00000001000012f8正好是personClass的地址,studentMetaClasssuperclass的值0x00000001000012d0正好是personMetaClass的地址,也就是superclass的值就是父类的地址

现在研究class的结构

class objc_class class_rw_t class_ro_t

objc_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对象studentClasssuperclassMJPersonclass对象,personClasssuperclassNSObjectclass对象。

studentClassData personClass

打印看到对象方法和属性信息都是存放在class对象中

studentMetaClassData personMetaClassData

打印看到类方法存放在meta-class对象中

面试题

对象的isa指针指向哪里?

OC的类信息存放在哪里?

上一篇下一篇

猜你喜欢

热点阅读