『iOS开发』 —— 探究OC对象本质

本文深入探讨Objective-C中对象、类与元类的关系,解析实例对象、类对象及元类对象的内存结构,揭示其内部工作原理,包括isa指针的作用及位运算细节。

文章首发地址见个人博客

探究OC对象本质

实例对象(Instance)

当我们调用NSObject *obj = [[NSObject alloc] init]生成的obj就是一个实例对象,实例对象在内存中的存储信息主要是一个指向**类对象(Class)**的isa指针和实例对象中所包含的成员变量。

@interface Person : NSObject
{
    int _age;
}
@end

@implementation
@end

对于一个NSObject实例对象,经过编译后转化为了一个名为NSObject_IMPL的结构体

struct NSObject_IMPL {
	Class isa;
};

对于一个继承自NSObjectPerson类,经过编译之后,生成了一个名为Person_IMPL的结构体,在这个结构体中有一个NSObject_IMPL的结构体,由此可以看出,子类中包含了父类的所有信息。

struct Person_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	int _age;
};
Person

类对象(Class)

类对象可以看做是对于实例对象的描述,在它的内存结构中包含了一个指向**元类对象(Meta Class)**的isa指针,一个指向父类的super_class指针,属性信息(nameversioninstance_size…),成员变量信息(objc_ivar_list),对象方法列表(objc_method_list),协议信息(objc_protocol_list)等。

每个类在内存中只有一个类对象,类对象可以通过Class objcClass = [NSObject class]获取,或者通过object_getClass(instance)获取,需要注意的是这里的instance必须是一个实例对象

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

元类对象(Meta Class)

元类对象可以看作是对类对象的描述,它的内存结构与类对象的内存结构完全一致,它在内存中的结构包括一个指向基类类对象(通常是NSObject)的isa指针,一个指向父类的super_class指针,类方法列表,以及其他信息。

下面这张图清晰地阐述了实例对象、类对象、元类对象三者之间的关系

caGEDV
对于一个实例对象,它的`isa`指针指向的是类对象,而类对象的`isa`指针指向的是元类对象,元类对象的`isa`指针指向的是基类元类对象,基类元类对象的`isa`指针指向的是自己。 对于一个类对象,它的`super_class`指针指向的是父类对象,基类对象的`super_class`指针指向的是`nil`。 对于一个元类对象,它的`super_class`指向的是父类的元类对象,而对于基类的元类对象来说,它的`super_class`指针指向的是基类的类对象(这一点很奇怪,如此说来,如果一个类只声明了一个类方法,没有实现,但实现了一个同名的对象方法,当这个类去调用类方法的时候,最终调用的是这个类的基类的对象方法) 下面来看一个例子:
@interface NSObject (HCTest)

- (void)test;

+ (void)test;

@end

@implementation NSObject (HCTest)

- (void)test
{
    NSLog(@"test");
}
@end

在这里声明了一个NSObject的分类test,在test中声明并实现了对象方法,只声明了同名的类方法,但是未实现对应的类方法。
这个时候我们调用一下NSObject的类方法test

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [NSObject test];
    }
    return 0;
}

发现最后的打印结果是

2020-08-03 23:03:23.921946+0800 test[1139:149862] test
Program ended with exit code: 0

说明在调用NSObject的类方法+ (void)test的时候最后确实是调用的- (void)test,很好地说明了元类之间的继承关系;

arm64架构和X86架构中,实例对象的isa指针并不直接指向类对象的内存地址,而是对isa指针进行了位运算,同样类对象的isa指针也不是直接就指向元类对象的内存地址,也是进行了位运算之后才是真正的元类对象的内存地址

jinIj5
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值