new
和delete
Objective-C 中没有new
和delete
这两个关键字(new
可以看作是一个函数,也就是alloc
+init
)。它们实际是被alloc
和release
所取代。
引用计数
内存管理是一个语言很重要的部分。在 C 和 C++ 中,内存块有一次分配,并且要有一次释放。这块内存区可以被任意多个指针指向,但只能被其中一个指针释放。Objective-C 则使用引用计数。对象知道自己被引用了多少次,这就像狗和狗链的关系。如果对象是一条狗,每个人都可以拿狗链拴住它。如果有人不想再管它了,只要丢掉他手中的狗链就可以了。只要还有一条狗链,狗就必须在那里;但是只要所有的狗链都没有了,那么此时狗就自由了。换做技术上的术语,新创建的对象的引用计数器被设置为 1。如果代码需要引用这个对象,就可以发送一个retain
消息,让计数器加 1。当代码不需要的时候则发送一个release
消息,让计数器减 1。
对象可以接收任意多的retain
和release
消息,只要计数器的值是正的。当计数器成 0 时,析构函数dealloc
将被自动调用。此时再次发送release
给这个对象就是非法的了,将引发一个内存错误。
这种技术并不同于 C++ STL 的auto_ptr
。Boost 库提供了一个类似的引用计数器,称为shared_ptr
,但这并不是标准库的一部分。
alloc
,copy
,mutableCopy
,retain
,release
明白了内存管理机制并不能很好的使用它。这一节的目的就是给出一些使用规则。这里先不解释autorelease
关键字,因为它比较难理解。
基本规则是,所有使用alloc
,[mutable]copy[WithZone:]
或者是retain
增加计数器的对象都要用[auto]release
释放。事实上,有三种方法可以增加引用计数器,也就意味着仅仅有有限种情况下才要使用release
释放对象:
- 使用
alloc
显式实例化对象; - 使用
copy[WithZone:]
或者mutableCopy[WithZone:]
复制对象(不管这种克隆是不是伪克隆); - 使用
retain
。
记住,默认情况下,给nil
发送消息(例如release
)是合法的,不会引起任何后果。
autorelease
不一样的autorelease
前面我们强调了,所有使用alloc
,[mutable]copy[WithZone:]
或者是retain
增加计数器的对象都要用[auto]release
释放。事实上,这条规则不仅仅适用于alloc
、retain
和release
。有些函数虽然不是构造函数,但也用于创建对象,例如 C++ 的二元加运算符(obj3 operator+(obj1, obj2)
)。在 C++ 中,返回值可以在栈上,以便在离开作用域的时候可以自动销毁。但在 Objective-C 中不存在这种对象。函数使用alloc
分配的对象,直到将其返回栈之前不能释放。下面的代码将解释这种情况:
// 第一个例子 -(Point2D*) add:(Point2D*)p1 and:(Point2D*)p2 { Point2D* result = [[Point2D alloc] initWithX:([p1 getX] + [p2 getX]) andY:([p1 getY] + [p2 getY])]; return result; } // 错误!这个函数使用了 alloc,所以它将对象的引用计数器加 1。 // 根据前面的说法,它应该被销毁。 // 但是这将引起内存泄露: [calculator add:[calculator add:p1 and:p2] and:p3]; // 第一个算式是匿名的,没有办法 release。所以引起内存泄露。 // 第二个例子 -(Point2D*) add:(Point2D*)p1 and:(Point2D*)p2 { return [[Point2D alloc] initWithX:([p1 getX] + [p2 getX]) andY:([p1 getY] + [p2 getY])]; } // 错误!这段代码实际上和上面的一样, // 不同之处在于仅仅减少了一个中间变量。 // 第三个例子 -(Point2D*) add:(Point2D*)p1 and:(Point2D*)p2 { Point2D* result = [[Point2D alloc] initWithX:([p1 getX] + [p2 getX]) andY:([p1 getY] + [p2 getY])]; [result release]; return result; } // 错误!显然,这里仅仅是在对象创建出来之后立即销毁了。
这个问题看起来很棘手。如果没有autorelease
的确如此。简单地说,给一个对象发送autorelease
消息意味着告诉它,在“一段时间之后”销毁。但是这里的“一段时间之后”并不意味着“任何时间”。我们将在后面的章节中详细讲述这个问题。现在,我们有了上面这个问题的一种解决方案:
-(Point2D*) add:(Point2D*)p1 and:(Point2D*)p2 { Point2D* result = [[Point2D alloc] initWithX:([p1 getX] + [p2 getX]) andY:([p1 getY] + [p2 getY])]; [result autorelease]; return result; // 更简短的代码是:return [result autorelease]; } // 正确!result 将在以后自动释放
3 评论
😯 😛 我来也,欢迎回访!
多谢多谢哦~
怎样知道变量的内存管理是不是autorelease类型的??
我在引用apple的cocoa框架中的一些方法生成实例对象时问题遇到这样的问题:
有时一个initWith..方法返回的变量内存管理方式为:autorelease
而大多数时候一个initWith..方法返回的变量内存管理方式为:retain
怎样知道变量的内存管理是不是autorelease类型的??
问题地址:http://www.cocoachina.com/bbs/read.php?tid-53543.html