前几天,我在iOS上搜索了更多关于内存管理的内容。这对我来说是一个很难理解的话题。因此,我决定在Objective-C中再深入一点——因为我已经在学习它了,以便更好地理解内存管理,并对这个主题有更深入的理解。例如在ARC之前是怎么样的?之后呢?ARC和垃圾收集器的区别是什么?…等。
介绍
当你开始处理对象时,你会注意到一件有趣的事情。
对象比基本类型(如整数和浮点数)对内存的要求更高。不管这些对象是来自你自己的类,还是来自任何Apple框架中的类,都是如此。
幸运的是,我们不必担心手动处理内存地址和手动分配或释放内存区域(这在某些语言中仍然适用)。相反,在Objective-C中,我们使用了一种叫做引用计数的东西。
引用计数是什么意思?
当应用程序启动时,对象可以使用内存区域。你创建了一个对象,并为该对象声明了内存区域。现在,保存对象的变量是指向该对象的指针。确切地说,是指向内存区域的指针。实际情况是,当对象被创建时它被赋予了所谓的保留计数或引用计数,它代表了特定对象的所有者数量。因此,我们可以把它想象成1,如下图所示:
被称为var的变量是一个指向内存块的指针。所以,在你的程序中,你使用那个指针并在它上面调用方法。但是当你到达代码块的末尾时,此变量不再可由程序的任何部分访问。retain count再次变为0,因为现在没有任何东西指向内存块。当retain count变为0时,运行时引擎会说,这是一块没人关心的内存。因此,它将释放这个内存块,并使其对其他对象可用。
什么地方可能出错?
这里惟一的问题是在创建对象并将它们从一个地方传递到另一个地方的情况下。所以不清楚指针是否还在作用域内。直到2011年添加ARC(自动引用计数)之前,你必须在使用完该对象时编写语句。
ARC之前
在ARC被添加到Objective-C之前我们必须做这样的事情:
MyClass *myObject = [[MyClass alloc] init];
[myObject myMethod]; // call methods
... // doing some stuff with the object
[myObject release]; // releasing the object
我们会写一些代码来创建一个对象。它会被创造出来,我们会调用那个对象的方法。但在某些情况下,我们需要显式地调用release语句。这就是导致retain count减少的原因。
这不是少量对象的问题。但是当你有成百上千的对象被创建、操作、用作参数、从一个对象传递到另一个对象时……你必须跟踪它们。如果你正在将一个对象从程序的一个区域传递到另一个区域,你甚至可能还不确定是否可以释放它。或者程序的其他部分会负责释放它,或者甚至在你完成之前就发布它。你也可以写,retain调用那个对象。但是你仍然需要匹配任何retain call和一个额外的release call。
基本上,在使用ARC之前,你必须设想应用程序将要经历的每一个可能的场景逻辑,以确保正确地管理所有对象的生存期,这不是那么容易。
ARC之后
幸运的是,当使用ARC时,你不再需要使用release autorelease retain调用。但重要的是要理解错误地编写这段代码的危险。
你可能会遇到的一个问题是你可能release释放得太快了。你会创建一个对象,有一个指向那个内存区域的指针,你会调用方法,在某个点你会释放它。但是,如果你仍然有一个有效的指针,那么实际上没有什么可以阻止你编写另一行代码来使用它。这将被称为悬浮指针。指针是存在的,但它所指向的对象在内存中不再有效,这可能会导致崩溃。
另一方面,你可能根本不会释放,你可能会创建一个对象。开始调用该对象的方法,然后只允许指针退出作用域并消失。但是你从来没有释放过这个对象,所以你只是开始占用越来越多的内存,这就是所谓的内存泄漏。
因此,编写大量这样的代码肯定很容易出现错误和问题。现在你可能想知道我们是否正在使用这个新的ARC特性,为什么我还要提到release和retain调用之类的东西。因为elease和retain调用的思想并没有消失,语言仍然做引用计数。
语言没有改变。不同之处在于,你根本不需要编写retain和release以及autorelease调用,因为编译器会这样做。ARC考虑到编译器已经变得很好了,任何时候你构建你的项目,编译器,在这种情况下,llvm,也就是Xcode在幕后使用的,能够决定你的代码的所有可能的路径。它基本上会遍历你的代码合成你需要的写,retain,release,和autorelease调用。
编译器所做的就是有效地编写与你自己编写的代码相同的代码,如果你非常非常擅长编写内存管理代码的话。
ARC不会改变你的源代码文件,但是当你编译一个项目时,这是编译器在做的,你正在使用ARC。
垃圾收集器和ARC的区别
ARC的效果与垃圾收集器非常不同。使用垃圾收集的语言通常被称为非确定性语言。这意味着你无法确切地知道对象何时被回收,因为它是由运行时由外部进程管理的。ARC允许我们完全确定。代码控制何时释放这些对象。只是释放它们的代码是由编译器编写的,而不是由你编写的。事实上,当你使用ARC时,你不仅不能写这些调用,你也不能写这些调用。如果你试着去做一个简单的release调用,你会得到一个错误。
小结
你需要意识到retain或release的概念。它仍然是发生在引擎盖下,但是,当然,如果你没有这样做,你应该感谢你不必再这样做了。
评论前必须登录!
注册