您的位置:首页 > 移动开发 > Objective-C

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

2013-12-09 09:41 871 查看


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
dealloc
method, 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. If
NSNotificationCenter
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.

A
dealloc
method looks like this:

Click here to view code image

- (void)dealloc {

CFRelease(coreFoundationObject);

[[NSNotificationCenter defaultCenter] removeObserver: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:

Click here to view code image

#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
close
should
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:

Click here to view code image

- (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


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.


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.


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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐