Effective Objective-C 2.0: Item 29: Understand Reference Counting
来源:互联网 发布:斗牛牛软件下载 编辑:程序博客网 时间:2024/06/06 00:41
Item 29: Understand Reference Counting
Objective-C uses reference counting for memory management, meaning that every object has a counter that is incremented and decremented. You increment the counter when you want to register your interest in keeping an object alive, and you decrement the counter when you have finished with it. When an object’s counter reaches zero, the object no longer has anything interested in it and is free to be destroyed. That is a brief overview; understanding the topic fully is crucial to writing good Objective-C code, even if you are going to be using ARC (see Item 30).
The garbage collector for use with Objective-C code written for Mac OS X is officially deprecated as of Mac OS X 10.8 and has never been available for iOS. Understanding reference counting is very important going forward because you can no longer rely on the garbage collector on Mac OS X and you never have, nor will you be able to, on iOS.
If you already use ARC, you should switch off the part of your brain that tells you that most of the code presented won’t compile. That’s true, in an ARC world. But this item is explaining reference counting from an Objective-C perspective, which is still going on with ARC, and to do so requires showing code that uses methods that are explicitly illegal under ARC.
How Reference Counting Works
Under reference-counting architectures, an object is assigned a counter to indicate how many things have an interest in keeping that object alive. This is referred to as the retain count in Objective-C but can also be referred to as the reference count. The following three methods declared on the NSObject
protocol can manipulate that counter to either increment it or decrement it:
retain Increment the retain count.
release Decrement the retain count.
autorelease Decrement the retain count later, when the autorelease pool is drained. (The autorelease pool is discussed further on page 150 and in Item 34.)
A method to inspect the retain count, called retainCount
, is generally not very useful, even when debugging, so I (and Apple) encourage you not to use it. See Item 36 for more information.
An object is always created with a retain count of at least 1. Interest in keeping the object alive is indicated by invoking the retain
method. When that interest has gone because the object is no longer required by a certain portion of code, release
or autorelease
is called. When the retain count finally reaches 0, the object is deallocated, meaning that its memory is marked for reuse. Once this has happened, any references to that object are no longer valid.
Figure 5.1 illustrates an object going through creation, a retain, and then two releases.
Many objects will be created throughout an application’s life cycle. These objects all become related to one another. For example, an object representing a person has a reference to string objects for the person’s names and might also have references to other person objects, such as in a set representing the friends of the person, thereby forming what is known as an object graph. Objects are said to own other objects if they hold a strong reference to them. This means that they have registered their interest in keeping them alive by retaining them. When they are finished with them, they release them.
In the Figure 5.2 object graph, ObjectA
is being referenced by both ObjectB
and ObjectC
. When both ObjectB
and ObjectC
have finished with ObjectA
, its retain count drops to 0 and it can be destroyed. Both ObjectB
and ObjectC
are being kept alive by other objects, which inturn are being kept alive by other objects. Eventually, if you went up the tree of what is referencing what, you would come to a root object. In the case of Mac OS X applications, this could be the NSApplication
object; in the case of iOS applications, the UIApplication
object.Both are singletons created when an application launches.
The following code will help you to understand this in practice:
Click here to view code image
NSMutableArray *array = [[NSMutableArray alloc] init];
NSNumber *number = [[NSNumber alloc] initWithInt:1337];
[array addObject:number];
[number release];
// do something with 'array'
[array release];
As explained previously, this code won’t compile under ARC, owing to the explicit calls torelease
. In Objective-C, a call to alloc
will result in the return of an object that is said to be owned by the caller. That is to say, the caller’s interest has already been registered because it used alloc
. However, it is important to note that this does not necessarily mean that the retain count is exactly 1. It might be more, since the implementation of either alloc
or initWithInt:
may mean that other retains have been made on the object. What is guaranteed, though, is that the retain count is at least 1. You should always think about retain counts in this way. You should never guarantee what a retain count is, only what effect your actions have had on the retain count: whether that is incremented or decremented.
The number
object is then added to the array. In doing this, the array also registers its interest by calling retain
on the number
object within the addObject:
method. At this point, the retain count is at least 2. Then, the number
object is no longer required by this code, so it is released. Its retain count is now back down to at least 1. At this point, the number
variable can no longer be safely used. The call to release
means that the object pointed to is no longer guaranteed to be alive. Of course, the code in this scenario makes it obvious that it will be alive after the call torelease
, since the array is also still referencing it. However, that should never be assumed, which means that you should not do something like this:
Click here to view code image
NSNumber *number = [[NSNumber alloc] initWithInt:1337];
[array addObject:number];
[number release];
NSLog(@"number = %@", number);
Even though the code will work in this scenario, it is not good practice. If for any reason thenumber
object were deallocated while calling release
as its retain count dropped to zero, the call to NSLog
would potentially crash. The reason I qualify that with “potentially” is that when an object is deallocated, its memory is simply returned to the available pool. If the memory has not been overwritten by the time the NSLog
runs, the object will still exist, and there won’t be a crash. For this reason, bugs where objects have been released too early are often difficult to debug.
To mitigate accidentally using an object that is no longer valid, you will often see a release
followed by nilling out the pointer. This ensures that nothing can access a pointer to a potentially invalid object, often referred to as a dangling pointer. For example, it can be done like this:
Click here to view code image
NSNumber *number = [[NSNumber alloc] initWithInt:1337];
[array addObject:number];
[number release];
number = nil;
Memory Management in Property Accessors
As described earlier, objects form an object graph by being linked together. The array in the example holds onto the objects it contains by retaining them. In the same way, other objects will hold onto other objects, often through the use of properties (see Item 6), which use accessors to get and set instance variables. If the property is a strong
relationship, the value of the property is retained. A setter accessor for such a property called foo
, backed by an instance variable called _foo
, would look like this:
- (void)setFoo:(id)foo {
[foo retain];
[_foo release];
_foo = foo;
}
The new value is retained, and the old value is released. Then the instance variable is updated to point to the new value. The order is important. If the old value was released before the new value was retained and the two values are exactly the same, the release would mean that the object could potentially be deallocated prematurely. The subsequent retain
could not resurrect the deallocated object, and the instance variable would be a dangling pointer.
Autorelease Pools
A feature that is important to Objective-C’s reference-counting architecture is what is known as autorelease pools. Instead of calling release
to immediately decrement an object’s retain count (and potentially deallocate it), you can also call autorelease,
which performs the release sometime later, usually the next time around the event loop, but it can also happen sooner (seeItem 34).
This feature is very useful, especially when returning an object from a method. In this case, you don’t always want to return it as owned by the caller. For example, consider the following method:
Click here to view code image
- (NSString*)stringValue {
NSString *str = [[NSString alloc]
initWithFormat:@"I am this: %@", self];
return str;
}
In this case, str
is returned with a +1 retain count, since the call to alloc
returns with a +1 count, and there is no balancing release. The meaning of +1 here is that you as the caller are responsible for one retain. You must somehow balance that one retain. This does not mean that the retain count is exactly 1, however. It might be more, but that is implementation detail within the method initWithFormat:
. All you need to worry about is balancing that one retain.
However, you cannot release str
inside the method, because it would then immediately be deallocated before being returned. So an autorelease
is used to indicate that the object should be released sometime later but guaranteed to be long enough in the future that the returned value can be retained by the caller if it needs to hold onto it. In other words, the object is guaranteed to be alive across the method call boundary. In fact, the release will happen when the outermost autorelease pool is drained (see Item 34), which, unless you have your own autorelease pools, will be next time around the current thread’s event loop. Applying this to thestringValue
method gives the following:
Click here to view code image
- (NSString*)stringValue {
NSString *str = [[NSString alloc]
initWithFormat:@"I am this: %@", self];
return [str autorelease];
}
Now the returned NSString
object will definitely be alive when it is returned to the caller. So the object can be used like this:
Click here to view code image
NSString *str = [self stringValue];
NSLog(@"The string is: %@", str);
No extra memory management is required here, since the str
object is returned autoreleased and therefore is balanced. Since the release from being in the autorelease pool won’t happen until next time around the event loop, the object does not need to be explicitly retained to be used in the log statement. However, if the object needs to be held onto, such as being set to an instance variable, the object needs to be retained and subsequently released:
Click here to view code image
_instanceVariable = [[self stringValue] retain];
// ...
[_instanceVariable release];
So autorelease
is a way of extending the lifetime of an object just enough so that it can survive across method call boundaries.
Retain Cycles
A common scenario to be aware of with reference counting is what is known as a retain cycle, which occurs when multiple objects reference one another cyclically. This leads to memory leaks because no object in the cycle will ever see its retain count drop to 0. Each object has at least one other object in the cycle maintaining a reference to it. In Figure 5.3, three objects all have a single reference to one of the other two objects. In this cycle, all retain counts are 1.
In a garbage-collected environment, this situation would usually be picked up as a so-called island of isolation. In such a scenario, the collector would deallocate all three objects. This luxury is not available under the reference-counting architecture of Objective-C. The problem is usually solved by using weak
references (see Item 33) or an external influence causing one of the objects to relinquish its retain of another object. In either case, the retain cycle is broken, and memory is no longer leaked.
Things to Remember
Reference-counting memory management is based on a counter that is incremented and decremented. An object is created with a count of at least 1. An object with a positive retain count is alive. When the retain count drops to 0, the object is destroyed.
As it goes through its life cycle, an object is retained and released by other objects holding references to it. Retaining and releasing increments and decrements the retain count, respectively.
- Effective Objective-C 2.0: Item 29: Understand Reference Counting
- Effective Objective-C 2.0: Item 30: Use ARC to Make Reference Counting Easier
- Effective Objective-C 2.0: Item 6: Understand Properties
- Effective Objective-C 2.0: Item 12: Understand Message Forwarding
- Effective Objective-C 2.0: Item 22: Understand the NSCopying Protocol
- Effective Objective-C 2.0: Item 37: Understand Blocks
- Effective Objective-C 2.0: Item 21: Understand the Objective-C Error Model
- <Effective Mordern C++>笔记:Item 2:Understand auto type deduction.
- <Effective Mordern C++>笔记:Item 3:Understand decltype .
- Understanding Automatic Reference Counting in Objective-C
- Effective Objective-C: Item11 Understand Role of obj_msgSend笔记
- Effective Objective-C 2.0:Item 7: Access Instance Variables
- Effective Objective-C 2.0:Item 14 Class Object
- Effective Objective-C 2.0: Item 16:Designated Initializer
- Effective Objective-C 2.0: Item 17: Implement the description Method
- Effective Objective-C 2.0:Item 20: Prefix Private Method Names
- Effective Objective-C 2.0:Item 26: Avoid Properties in Categories
- Effective Objective-C 2.0: Item 36: Avoid Using retainCount (Deprecated)
- Web负载均衡解决方案 HAproxy架构图
- ubuntu安装Hadoop-1.2.1
- Object9
- JAVA规则引擎 -- Drools
- 内部类与匿名内部类
- Effective Objective-C 2.0: Item 29: Understand Reference Counting
- POJ 3176 Cow Bowling (简单DP)
- 异常11
- Jeecg Tag的扩展参数,及其虚拟布局
- Working Practice-表达代码的方法
- Drools5集成Spring3
- Codeforces Round #218 (Div. 2)
- FairScheduler的任务调度机制——assignTasks
- HDU 2049 不容易系列之(4)——考新郎 (错排)