iOS 底层原理探索:深挖对象内存布局(上)
2023-10-15 00:03:14
对象的底层奥秘:揭开内存布局的面纱
对象内存布局的组成部分
深入 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;
}
常见问题解答
- isa 指针和类指针有什么区别?
isa 指针指向对象的元类,而类指针直接指向类本身。
- 引用计数的原理是什么?
引用计数记录着指向对象的引用数量,当引用计数为 0 时,对象将被销毁。
- ARC 和非 ARC 环境下的内存管理有何不同?
在 ARC 环境下,编译器自动管理内存,而非 ARC 环境下需要手动管理。
- 如何查看对象的内存布局?
可以使用 lldb 命令来查看对象的内存布局,例如:p (Person *)person
- 对象销毁后会发生什么?
对象销毁后,其占用的内存会被释放,可供其他对象使用。