返回

揭开 OC 底层的秘密:对象的本质与 isa

IOS

OC 对象的本质

clang 简介

clang 是一个 C 语言家族(包括 C、C++、Objective-C)的编译器前端,它也是 LLVM 编译器套件的一部分。clang 可以将 OC 代码转换为低级的中间表示 (IR),然后由 LLVM 后端编译为机器代码。通过研究 clang 的中间表示,我们可以一窥 OC 对象在底层是如何表示的。

clang 使用示例

让我们创建一个新的 macOS 命令行工具工程,并使用 clang 编译器对 OC 代码进行编译:

// main.m
#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    NSString *_name;
    int _age;
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;

@end

@implementation Person

- (instancetype)initWithName:(NSString *)name age:(int)age
{
    self = [super init];
    if (self) {
        _name = [name copy];
        _age = age;
    }
    return self;
}

- (void)dealloc
{
    [_name release];
    [super dealloc];
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] initWithName:@"John" age:30];
        NSLog(@"%@ is %d years old.", person.name, person.age);
    }
    return 0;
}

在终端中运行以下命令来编译代码:

clang -rewrite-objc main.m

这将生成一个名为 main.m.cpp 的文件,其中包含代码的重写版本,其中包含 clang 的中间表示。

isa 指针

isa 指针是 OC 对象中一个至关重要的组成部分。它指向该对象的类对象,类对象包含有关该类及其方法的所有信息。isa 指针位于对象内存布局的开头,在编译时由编译器插入。

通过检查 main.m.cpp 中的中间表示,我们可以看到 isa 指针的声明:

struct _objc_object {
    Class isa;
    // ... 其他实例变量 ...
};

在我们的示例中,Person 对象的 isa 指针指向 Person 类对象:

Person *person = [[Person alloc] initWithName:@"John" age:30];
// person 的 isa 指针指向 Person 类对象

对象内存布局

OC 对象的内存布局分为三个部分:

  1. isa 指针: 指向类对象的指针。
  2. 实例变量: 存储对象状态的变量。
  3. 对齐填充: 确保对象在内存中对齐,以提高性能。

示例中的 Person 对象的内存布局如下:

| isa 指针 | _name 实例变量 | _age 实例变量 | 对齐填充 |

isa 指针的作用

isa 指针在 OC 运行时中扮演着至关重要的角色:

  1. 消息转发: 当一个消息发送给对象时,如果该对象没有实现该方法,isa 指针将指向父类的类对象,从而继续消息转发过程。
  2. 动态绑定: 在运行时确定要调用的方法。isa 指针指向不同的类对象,可以动态地确定要调用哪个实现。
  3. 内存管理: isa 指针用于确定对象的类型,以便在释放对象时调用正确的析构函数。

结论

通过深入了解 OC 对象的本质和 isa 指针的作用,我们对 OC 语言和运行时的理解得到了提升。这些底层知识对于编写高效、健壮的 OC 代码至关重要。通过掌握这些概念,我们可以优化应用程序的性能,并编写出更优雅、更可维护的代码。