返回

iOS 底层原理探索:深挖对象内存布局(上)

IOS

对象的底层奥秘:揭开内存布局的面纱

对象内存布局的组成部分

深入 iOS 底层世界,首先需要了解对象的内存布局,它由以下四个部分组成:

  • isa 指针: 犹如一张身份卡,指向对象的元类,表明对象所属的类型。
  • 实例变量: 对象自身的宝库,存储着成员变量和属性这些数据珍宝。
  • 引用计数: 仿佛一个计数器,记录着对象的引用次数,决定着它何时寿终正寝。
  • 内存管理结构: 担任着管家角色,负责管理对象的内存分配和回收。

isa 指针:对象身份的认证

isa 指针是一个 32 位的指针,指向上级——对象的元类。元类是类的元数据仓库,包含了类名、父类和方法列表等重要信息。有了 isa 指针,便能快速识别对象的类型。

实例变量:对象的宝藏

实例变量是对象具体数据的家园,包括类定义的成员变量和属性。它们按类中声明的顺序排列,仿佛一个个整齐的抽屉。

引用计数:生死簿的记录者

引用计数是一个 32 位的整数,记录着指向对象的引用数量。当对象诞生时,它携带一个 1 的引用计数。当有对象引用它时,计数增加 1;当引用被释放时,计数减少 1。当引用计数归零时,对象的生命也就走到了尽头。

内存管理结构:幕后的操控者

内存管理结构是内存分配和回收的幕后英雄。在 ARC(自动引用计数)环境下,编译器担负起管理任务,在对象创建和销毁时自动增减引用计数。

在非 ARC 环境下,需要手动操作,使用 CFRetain() 和 CFRelease() 函数增加或减少对象的引用计数。

内存布局的深入剖析

isa 指针:身份的钥匙

isa 指针是对象的身份证,它决定了对象的类型。通过 isa 指针,我们可以获取对象的元类,进而了解类的详细信息。

实例变量:数据的宝库

实例变量是对象存储数据的容器,包括成员变量和属性。它们按照声明顺序排列,便于访问和修改。

引用计数:生死的计时器

引用计数是决定对象命运的计时器。当引用计数为 0 时,对象将被销毁。编译器自动管理引用计数,开发者无需手动操作。

内存管理结构:幕后的守护者

在 ARC 环境下,编译器负责内存管理,在对象创建和销毁时自动增减引用计数。在非 ARC 环境下,需要手动管理,使用 CFRetain() 和 CFRelease() 函数控制引用计数。

代码示例

#import <Foundation/Foundation.h>

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

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] initWithName:@"John" age:25];
        
        // isa 指针指向 Person 类的元类
        NSLog(@"isa 指针:%@", object_getClass(person));
        
        // 获取实例变量的值
        NSLog(@"姓名:%@,年龄:%d", person->_name, person->_age);
        
        // 手动增加引用计数
        CFRetain(person);
        
        // 手动减少引用计数
        CFRelease(person);
    }
    return 0;
}

常见问题解答

  1. isa 指针和类指针有什么区别?

isa 指针指向对象的元类,而类指针直接指向类本身。

  1. 引用计数的原理是什么?

引用计数记录着指向对象的引用数量,当引用计数为 0 时,对象将被销毁。

  1. ARC 和非 ARC 环境下的内存管理有何不同?

在 ARC 环境下,编译器自动管理内存,而非 ARC 环境下需要手动管理。

  1. 如何查看对象的内存布局?

可以使用 lldb 命令来查看对象的内存布局,例如:p (Person *)person

  1. 对象销毁后会发生什么?

对象销毁后,其占用的内存会被释放,可供其他对象使用。