首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》3.3 指针变量与对象所有权

关灯直达底部

指针变量暗含了对其所指向的对象的所有权(ownership)。

•当某个方法(或函数)有一个指向某个对象的局部变量时,可以称该变量拥有(own)该变量所指向的对象。

•当某个对象有一个指向其他对象的实例变量时,可以称该对象拥有该实例变量所指向的对象。

请读者回想RandomItems应用,应用在main函数中创建一个NSMutableArray对象,然后创建10个BNRItem对象并加入该数组。图3-2显示的是RandomItems中的对象及指向这些对象的指针。

图3-2  RandomItems对象图(图中有两个BNRItem对象)

因为指向NSMutableArray对象的items指针是main函数的局部变量,所以main函数拥有相应的NSMutableArray对象。

NSMutableArray对象拥有其包含的所有BNRItem对象。对NSMutableArray这种collection类,其对象保存的是指向对象的指针,而不是对象自身。这些指针暗含了以下所有权:数组对象拥有其包含的所有对象。

最后,每一个BNRItem对象都拥有其实例变量所指向的对象。

对象所有权概念可以帮助我们决定是否应该释放某个对象并回收该对象占有的内存。

•如果某个对象没有拥有者,就应该将其释放掉。没有拥有者的对象是孤立的,程序无法向其发送消息。保留这样的对象只会浪费宝贵的内存空间,导致内存泄露(memory leak)问题。

•如果某个对象有一个或多个拥有者,就必须保留不能释放。如果释放了某个对象,但是其他对象或方法仍然有指向该对象的指针(准确地说,是指向该对象被释放前的地址),那么向该指针指向的对象发送消息就会使应用崩溃。释放正在使用的对象的错误称为过早释放。指向不存在的对象的指针称为空指针(dangling pointer)或者空引用(dangling reference)。

哪些情况会使对象失去拥有者

下列情况会使对象失去拥有者:

•当程序修改某个指向特定对象的变量并将其指向另一个对象时。

•当程序将某个指向特定对象的变量设置为nil时。

•当程序释放对象的某个拥有者时。

•当从collection类中(例如数组)删除对象时。

下面逐个介绍这4种情况。

修改指针变量

以BNRItem为例,假设有某个BNRItem对象,其实例变量_itemName所指向的NSString对象是@“Rusty Spork”(生锈的叉勺)。如果有人将这个叉勺抛光,去除锈迹,那么“生锈的叉勺”就变成“闪闪发光的叉勺”(Shiny Spork)。这时,就需要将_itemName指向一个不同的NSString对象(见图3-3)。

图3-3  修改指针变量所指向的对象

当应用修改_itemName的值,将其从指向@“Rusty Spork”的地址改为指向@“Shiny Spork”的地址时,@“Rusty Spork”就会失去一个拥有者。如果此时@“Rusty Spork”已经没有其他拥有者了,它就应该被释放。

将指针变量的值设为nil

如果某个指针变量的值是nil,就代表这个指针没有指向任何对象。以BNRItem为例,假设有一个代表某台电视机的BNRItem对象,当这台电视机的序列号被刮去时,就应该将相应BNRItem对象的实例变量_serialNumber设为nil。而这个_serialNumber曾经指向的NSString对象将失去一个拥有者。

对象的拥有者被释放

对象的拥有者被释放时,就会失去一个拥有者,所以释放一个对象可能会造成其他对象失去拥有者。

方法或函数可以通过局部变量成为对象的拥有者。当程序执行完某个方法,将其帧弹出栈时,就会释放该方法的所有局部变量。在这些变量中,如果有指向其他对象的指针变量,那么这些对象就会失去一个拥有者。

从collection对象中删除对象

collection对象会拥有其包含的对象。当程序将某个对象移出可修改的collection对象(例如NSMutableArray对象)时,这个被移出的对象就会失去一个拥有者,代码如下:

[items removeObject,item];  //item所指向的对象会失去一个拥有者

需要注意的是,当某个对象失去一个拥有者时,程序不一定会释放这个对象。只要还有另一个指向该对象的指针,程序就会保留这个对象。但是当某个对象失去最后一个拥有者时,就一定会被释放。

所有权链(Ownership chains)

因为对象可以拥有其他对象,后者也可以再拥有别的对象,所以释放一个对象可能会产生连锁反应,导致多个对象失去拥有者,进而释放对象并归还内存。

RondomItems中就有一个现成的例子,请读者先回顾RandomItems的对象图(见图3-2)。

在main.m中,RandomItems会先输出包含多个BNRItem对象的items数组,然后将items变量设置为nil。当程序将items变量设置为nil时,会导致其指向的NSMutableArray对象失去唯一的拥有者,并因此被释放。

但这只是开始。当程序释放NSMutableArray对象时,也会释放其包含的所有指向BNRItem对象的指针。一旦程序释放这些指针变量,相应的BNRItem对象将失去最后一个拥有者,并被程序释放。

程序在释放BNRItem对象时,也会释放其实例变量。在这些实例变量中,如果是指向其他对象的指针变量,这些对象就会失去一个拥有者。因为这些对象只有BNRItem对象一个拥有者,所以也会被释放。

下面为RandomItems加入测试代码,输出上述释放过程。NSObject实现了一个名为dealloc的方法。当某个对象即将被释放时,程序会调用该对象的dealloc方法。通过覆盖BNRItem的dealloc方法,可以在程序释放BNRItem对象时向控制台输出一行提示。

打开RandomItems项目,选中BNRItem.m,然后覆盖dealloc,代码如下:

- (void)dealloc

{

NSLog(@/"Destroyed: %@/", self);

}

在main.m中加入下面这行代码:

NSLog(@/"Setting items to nil.../");

items = nil;

构建并运行应用,和之前一样,控制台应该会输出BNRItem数组的描述信息。新增加的提示内容,首先是“items将被设置为nil”(Setting items to nil…),然后是针对每个BNRItem对象的“某对象已经被释放”(Destroyed: %@)。

最后,程序会释放所有的对象,只剩下main函数。程序仅仅是将items设置为nil,就完成了这些自动的内存清理和回收工作。使用ARC的好处可见一斑。