📜  Obj-C内存管理

📅  最后修改于: 2020-11-03 16:04:33             🧑  作者: Mango


在任何编程语言中,内存管理都是最重要的过程之一。它是在需要时分配对象内存并在不再需要时释放对象内存的过程。

管理对象内存关系到性能。如果应用程序不释放不需要的对象,则其内存占用量会增加,并且性能会受到影响。

Objective-C内存管理技术可以大致分为两种类型。

  • “手动保留释放”或MRR
  • “自动引用计数”或ARC

“手动保留释放”或MRR

在MRR中,我们通过自己跟踪对象来显式管理内存。这是使用Foundation类NSObject与运行时环境一起提供的模型(称为引用计数)来实现的。

MRR和ARC之间的唯一区别是,保留和释放是由我们在前者中手动处理的,而由我们在后者中自动处理。

下图代表了Objective-C中内存管理的工作方式示例。

Objective-C内存管理

上图显示了A类对象的内存生命周期。如您所见,保留计数显示在对象的下方,当对象的保留计数变为0时,该对象被完全释放,其内存被释放以供其他对象使用。

类首先使用NSObject中可用的alloc / init方法创建对象。现在,保留计数变为1。

现在,类B保留了类A的对象,并且类A的对象的保留计数变为2。

然后,类C复制该对象。现在,它被创建为类A的另一个实例,并且实例变量的值相同。此处,保留计数为1,而不是原始对象的保留计数。这由图中的虚线表示。

类C使用释放方法释放了复制的对象,并且保留计数变为0,因此该对象被破坏。

对于初始的A类对象,保留计数为2,必须将其释放两次才能销毁。这是通过Class A和Class B的release语句将保留计数分别递减为1和0来完成的。最后,对象被销毁。

MRR基本规则

  • 我们拥有我们创建的任何对象:我们使用名称以“ alloc”,“ new”,“ copy”或“ mutableCopy”开头的方法创建对象

  • 我们可以使用keep来获得对象的所有权:通常可以保证接收到的对象在接收该对象的方法中保持有效,并且该方法还可以安全地将该对象返回给其调用者。我们在两种情况下使用保留-

    • 在访问器方法或init方法的实现中,要获取我们要存储为属性值的对象的所有权。

    • 防止由于其他操作的副作用而使对象无效。

  • 当我们不再需要它时,我们必须放弃对自己拥有的对象的所有权:我们通过向对象发送释放消息或自动释放消息来放弃对对象的所有权。因此,在可可术语中,放弃对象的所有权通常称为“释放”对象。

  • 您不得放弃不拥有的对象的所有权:这只是先前明确规定的先前策略规则的必然结果。

#import 

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
  [super dealloc];
}

@end

int main() {
   
   /* my first program in Objective-C */
   SampleClass *sampleClass = [[SampleClass alloc]init];
   [sampleClass sampleMethod];
   
   NSLog(@"Retain Count after initial allocation: %d", 
   [sampleClass retainCount]);
   [sampleClass retain];
   
   NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
   [sampleClass release];
   NSLog(@"SampleClass dealloc will be called before this");
   
   // Should set the object to nil
   sampleClass = nil;
   return 0;
}

当我们编译上面的程序时,我们将得到以下输出。

2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this

“自动引用计数”或ARC

在自动引用计数或ARC中,系统使用与MRR相同的引用计数系统,但它会在编译时为我们插入适当的内存管理方法调用。强烈建议我们将ARC用于新项目。如果我们使用ARC,尽管在某些情况下可能会有所帮助,但通常无需了解本文档中描述的基本实现。有关ARC的更多信息,请参阅过渡到ARC发行说明。

如上所述,在ARC中,由于编译器会注意释放和保留方法,因此我们无需添加。实际上,Objective-C的基本过程仍然相同。它在内部使用保留和释放操作,使开发人员更容易编写代码而无需担心这些操作,这将减少编写的代码量和内存泄漏的可能性。

在Mac OS-X中,还有另一个称为垃圾收集的原理与MRR一起使用,但是自从OS-X Mountain Lion中弃用垃圾回收以来,就没有与MRR一起讨论过。另外,iOS对象从没有垃圾收集功能。借助ARC,在OS-X中也无需使用垃圾收集。

这是一个简单的ARC示例。请注意,此功能不适用于在线编译器,因为它不支持ARC。

#import 

@interface SampleClass:NSObject
- (void)sampleMethod;
@end

@implementation SampleClass
- (void)sampleMethod {
   NSLog(@"Hello, World! \n");
}

- (void)dealloc  {
  NSLog(@"Object deallocated");
}

@end

int main() {
   /* my first program in Objective-C */
   @autoreleasepool {
      SampleClass *sampleClass = [[SampleClass alloc]init];
      [sampleClass sampleMethod];
      sampleClass = nil;
   }
   return 0;
}

当我们编译上面的程序时,我们将得到以下输出。

2013-09-28 04:45:47.310 demo[8385] Hello, World!
2013-09-28 04:45:47.311 demo[8385] Object deallocated