5.使用autorelease(Using autorelease)
内存管理时最好遵循谁拥有,谁释放的原则,(You created items in main, use it there, and release it there:你在main函数中创建了items对象,在这个函数中使用,在这个函数中释放)。否则容易造成忘记释放或者不确定是否要释放的问题。当你在一个方法中创建了一个对象,没有用这个对象,只是想吧它传出去,那么应该怎样管理呢?(在哪里释放?),这种情况经常出现在便捷方法(返回实例的类方法)中(This is often the case with convenience methods – class methods that return instances of the class.),如:
+ (id)randomPossession
{
... Create random variables ...
Possession *newPossession = [[self alloc]
initWithPossessionName:randomName
valueInDollars:randomValue
serialNumber:randomSerialNumber];
// If we release newPossession here,
// the object is deallocated before it is returned 如果我们在这里释放,对象就会在返回之前被释放
return newPossession;
// If we release newPossession here, this code is never executed.
}
你可能会想在其他地方释放如:
for(int i = 0; i < 10; i++)
{
Possession *p = [Possession randomPossession];
[items addObject:p];
// Don't do this!
[p release];
}
This is a very bad idea.The responsibility that is the core of reference counting includes not releasingobjects that don’t belong to you. 引用计数的核心机制规定不释放不属于你的对象。你需要一种方法就是:暂时不释放,但已经不再想作为这个对象的拥有者了(You need some way of saying “Don’t release this object yet, but I don’t want to
be an owner of it anymore.”)。为了解决这个问题你可以向对象发送autorelease消息,当对象接收到这个消息后并不会立刻被释放,而是加入到一个NSAutoreleasePool的实例当中,周期性的,当自动释放池被排干,在这个池里的对象会收到release消息,然后移出这个池。(Periodically, the autorelease pool is drained; it sends the message release
to the objects in the pool and then removes them.)那么,我们会问:”放进池中的对象什么时候被释放呢?“这里引出一个运行环(run loop)的概念。
当一个IOS应用运行时,存在一个运行环(run loop)在不断地循环 (When an iOS application is running, there is a run loop that is continually cycling)这个循环在检查(或等待)事件,像一些点击或定时器这样的事件(This run loop checks for events, like a touch or a timer firing)。当一个事件发生了,应用程序会中断循环去处理你所写的处理事件地方法(Whenever an event occurs, the application breaks from the run loop and processes that event by calling the methods you have written in your classes)。当这个事件方法的代码执行完成时,应用程序会返回这个环,当这个环结束时,池里的对象就会收到release消息。
autorelease的返回值是一个接收消息的实例,所以可以这样写
// Because autorelease returns the object being autoreleased, we can do this:
NSObject *x = [[[NSObject alloc] init] autorelease];
所以在便捷方法中改为:
{
Possession *newPossession =
[[self alloc] initWithPossessionName:randomName
valueInDollars:randomValue
serialNumber:randomSerialNumber];
return [newPossession autorelease];
}
6.存取器和内存管理(Accessors and memory management)
在设置器中,我们为了保留对象不被释放,会在方法中向传入的对象发送retain消息如:
- (void)setPossessionName:(NSString *)str
{
[str retain];
possessionName = str;
}
但这样做存在缺陷,比如:
Possession *p = [[Possession alloc] init];
[p setPossessionName:@"White Sofa"];
// Wait, no it isn't...
[p setPossessionName:@"Off-white Sofa"];
这个时候就会导致内存泄漏,所以设置器中修改如下:
- (void)setPossessionName:(NSString *)str
{
[str retain];
[possessionName release];
possessionName = str;
}
插入一个知识点:当你向空指针(nil)发送消息时,并不会产生什么不好的影响。
7.实现dealloc(Implementing dealloc)
当你定义的类中有属性为对象时,因为在设置器方法中保留了对象(retain),所以要在dealloc方法中释放,即在对象销毁之前释放对象所拥有的一些对象类型的实例变量(you must release them before you lose your pointers to them)。执行如下:
- (void)dealloc
{
[possessionName release];
[serialNumber release];
[dateCreated release];
[super dealloc];
}
要注意父类的dealloc实现总是在方法的最后,因为父类中也有对象类型的实例变量要释放,直到在最后执行NSObject的dealloc方法就会返回对象的内存给堆(Then, because you call the superclass’s implementation, it goes up its class hierarchy and releases any instance variables of its superclass. In the end, the imp ementation of dealloc in NSObject returns the object’s memory to the heap.)。(想想之前在创建时是调用alloc方法来分配内存的,而alloc方法在NSObject来定义实现的,类知道要分配的字节的数量(在前面说过),所以也在NSObject来返回内存给堆)。
好吧,先写那么多,接下来讨论一下@property中涉及的其他参数,重点是copy,还有数据块,栈的一些知识。