Effective Objective-C 2.0: Item 31: Release References and Clean Up Observation State Only in deallo

Item 31: Release References and Clean Up Observation State Only in dealloc

An object going through its life cycle eventually ends up being deallocated, which is where the dealloc method enters. It is called exactly once during the life cycle of an object: when its retain count drops to zero. When exactly it gets called is not guaranteed, though. Also, you may think that you know when it’s going to be called from manually seeing where retains and releases are. But in practice, any library could be manipulating the object without your knowing, causing deallocation to happen at another time. You should never call dealloc yourself. The runtime will call it at exactly the right time for you. Also, after dealloc has been called on an object, that object is no longer valid, and subsequent method calls are invalid.

So what should you do in dealloc then? The main thing to do is to release any references that the object owns. This means releasing any Objective-C objects, something that ARC automatically adds for you into the deallocmethod, through the .cxx_destruct automatic method (see Item 30). Any other non-Objective-C objects that the object owns also need releasing. For instance, CoreFoundation objects need to be explicitly released, since they are pure C APIs.

Another usual thing to do in a dealloc method is to clean up any observation behavior that has been set up. IfNSNotificationCenter has been used to register the object for certain notifications, this is often a good place to unregister for notifications so that they are not attempted to be sent to a deallocated object, which would certainly cause an application to crash.

dealloc method looks like this:

- (void)dealloc {
    CFRelease(coreFoundationObject);
    [[NSNotificationCenter defaultCenterremoveObserver:self];
}

Note that when using manual reference counting rather than ARC, [super dealloc] should be the last thing done. ARC automatically enforces this, which is another reason why it is a lot easier and safer to use than manual reference counting. With manual reference counting, this method would also have to manually release every Objective-C object owned by the object.

That said, you should certainly not free resources that are potentially expensive or scarce within the system. Such resources are file descriptors, sockets, or large blocks of memory. The dealloc method should not be relied on to be called at any specific time, since something you hadn’t realized might be holding onto the object. In such a situation, you are keeping hold of scarce system resources longer than you need to, which is undesirable. It is usual in these scenarios to implement another method that should be called when the application has finished with the object. The resources’ life cycles are then made deterministic.

An example of an object that might need a cleanup method is one that manages a socket connection to a server. Perhaps it is a connection to a database. Such a class’s interface may look like this:

#import <Foundation/Foundation.h>

@interface EOCServerConnection : NSObject
- (void)open:(NSString*)address;
- (void)close;
@end

The contract for this class would be that the open: method is called to open the connection; then, when finished with the connection, the application calls close. The close must happen before the connection object is deallocated; otherwise, it is deemed to be a programmer error, just as you have to balance retains and releases with reference counting.

Another reason for cleaning up resources in another cleanup method is that the dealloc method is in fact not guaranteed to be run for every created object. There is the edge case of objects that are still around when an application terminates. These objects do not receive the dealloc message. Instead, they are destroyed by the fact that a terminated application’s resources are returned to the operating system. It is an optimization not to call the dealloc method. But this means that you cannot guarantee that it is always called for every object. Mac OS X and iOS applications both have within their application delegates a method that is called on application termination. This method can be used to run any cleanup methods on objects that need to be guaranteed to be cleaned up.

In the case of Mac OS X, the method called at application termination is on NSApplicationDelegate:

- (void)applicationWillTerminate:(NSNotification *)notification

In the case of iOS, the method is on UIApplicationDelegate:

- (void)applicationWillTerminate:(UIApplication *)application

With regard to the cleanup method for objects that manage resources, this should also be called in dealloc to mitigate the case in which the cleanup method was not called. If this does happen, it is often a good idea to output a log line to indicate that a programmer error has occurred. It is a programmer error because closeshould have been called before the object was deallocated; otherwise, the close method is irrelevant. This log line will alert the programmer to rectify the problem. It’s still good practice to close the resources in dealloc in order to avoid leaks. An example of such a close and dealloc method is as follows:

- (void)close {
    /* clean up resources */
    _closed = YES;
}

- (void)dealloc {
    if (!_closed) {
        NSLog(@"ERROR: close was not called before dealloc!");
        [self close];
    }
}

Instead of simply logging an error if the close method is not called, you might decide to throw an exception to indicate that a serious programmer error has occurred.

Another thing to be aware of and avoid in dealloc methods is calling other methods. In the preceding example, of course, a method is called in dealloc. But this is a special case: to detect programmer error. It is not ideal to have to call any other methods at all, because the object being deallocated is in a winding-down state. If the other method happens to perform work asynchronously or calls methods that themselves do, the object being deallocated could be completely dead by the time those methods finish doing their work. This can cause all sorts of problems and often results in an application crash because it calls back to tell the object that it has finished. If the object is dead, this call will fail.

Also, the dealloc method is called on the thread in which the final release that caused the retain count to zero occurred. Some methods are required to be run in a certain thread, such as the main thread. If these methods are called from dealloc, there is no safe way to ensure that it is run on the correct thread. Any usual code to force it to be run on the correct thread is not at all safe, because the object is in a deallocating state, and the runtime has already started altering its internal data structures to indicate this.

The avoidance of method calls in dealloc should also go for property accessors, which can be overridden and therefore themselves try to perform work that is unsafe to do during deallocation. Alternatively, the property may be being observed through Key-Value Observation (KVO), and the observer may try to do some work, such as attempting to retain the object, using the object that is being deallocated. Doing so would cause the runtime to get in a completely inconsistent state, and strange crashes would likely result.

Things to Remember

Image The dealloc method should be used only to release references to other objects and to unregister anything that needs to be, such as Key-Value Observing (KVO) or NSNotificationCenter notifications.

Image If an object holds onto system resources, such as file descriptors, there should be a method for releasing these resources. It should be the contract with the consumer of such a class to call this close method when finished using the resources.

Image Method calls should be avoided in dealloc methods in case those methods try to perform asynchronous work or end up assuming that the object is in a normal state, which it won’t be.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值