>Objective-C 内存管理
内存管理是任何编程语言中最重要的过程之一。 这是在需要时分配对象内存并在不再需要时释放对象内存的过程。
管理对象内存是性能问题; 如果应用程序不释放不需要的对象,它的内存占用量就会增加,性能也会受到影响。
Objective-C 内存管理技术大致可分为两类。
- "手动保留-释放"或 MRR
- "自动引用计数"或 ARC
"手动保留-释放"或 MRR
在 MRR 中,我们通过自己跟踪对象来显式管理内存。 这是使用称为引用计数的模型实现的,基础类 NSObject 结合运行时环境提供该模型。
MRR 和 ARC 的唯一区别在于,前者的保留和释放由我们手动处理,而后者自动处理。
下图展示了 Objective-C 中内存管理工作原理的示例。
A类对象的内存生命周期如上图所示。 可以看到,保留计数显示在对象下方,当一个对象的保留计数变为 0 时,该对象将被完全释放,其内存将被释放供其他对象使用。
Class A 对象首先使用 NSObject 中可用的 alloc/init 方法创建。 现在,保留计数变为 1。
现在,B 类保留了 A 类的对象,A 类对象的保留计数变为 2。
然后,C 类复制该对象。 现在,它被创建为 A 类的另一个实例,实例变量具有相同的值。 这里,保留计数为 1 而不是原始对象的保留计数。 这由图中的虚线表示。
C 类使用 release 方法释放复制的对象,保留计数变为0,因此对象被销毁。
对于初始的 A 类对象,保留计数为 2,必须释放两次才能将其销毁。 这是通过 A 类和 B 类的发布语句完成的,它们分别将保留计数递减为 1 和 0。 最后,对象被销毁。
MRR 基本规则
我们拥有我们创建的任何对象:我们使用名称以"alloc"、"new"、"copy"或"mutableCopy"开头的方法创建对象
我们可以使用保留获取对象的所有权:接收到的对象通常保证在接收它的方法内保持有效,并且该方法也可以安全地将对象返回给它的调用者。 我们在两种情况下使用 retain −
在访问器方法或 init 方法的实现中,获取我们想要存储为属性值的对象的所有权。
防止对象因其他操作的副作用而失效。
当我们不再需要它时,我们必须放弃我们拥有的对象的所有权:我们通过向对象发送释放消息或自动释放消息来放弃对象的所有权。 因此,在 Cocoa 术语中,放弃对象的所有权通常称为"释放"对象。
您不得放弃不属于您的对象的所有权:这只是先前明确规定的政策规则的必然结果。
#import <Foundation/Foundation.h> @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 的更多信息,请参阅 https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
如上所述,在 ARC 中,我们不需要添加 release 和 retain 方法,因为编译器会处理这些方法。 实际上,Objective-C 的底层流程还是一样的。 它在内部使用了 retain 和 release 操作,使开发人员更容易编写代码而不必担心这些操作,这将减少编写的代码量和内存泄漏的可能性。
还有一个称为垃圾收集的原则,它与 MRR 一起在 Mac OS-X 中使用,但由于它在 OS-X Mountain Lion 中被弃用,因此没有与 MRR 一起讨论。 此外,iOS 对象从来没有垃圾收集功能。 对于 ARC,在 OS-X 中也没有使用垃圾收集。
这是一个简单的 ARC 示例。 请注意,这不适用于在线编译器,因为它不支持 ARC。
#import <Foundation/Foundation.h> @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