循环引用,这里主要指iOS中的Retain Cyles。如下图:
循环引用其实并不坏,但是我们应该尽量避免,因为如果不正确的处理,他们经常会导致内存泄露。当我们将上图中的垂直方向的两个引用关系删除时,由于下面两个对象互相引用,结果是他们不会被释放,造成内存泄露。如下图:
虽然循环引用,在我们精心释放的情况下,也不一定会内存泄露,例如,我们在释放NSTableView时,注意删除NSTableView对Delegate Object的强引用关系。
但是在项目复杂的情况下,又没有一个人对所有的类都了解,最好的方法是我们用约定的编码方式,避免产生循环引用。
于是,产生了弱引用这种概念:如下图
注意:@property (weak) id delegate; 这句中的weak, 他指明NSTableView并不负责保持delegate这个对象,delegate这个对象的销毁由外部控制,NSTableView只是使用他。
下面是对循环引用问题的讨论和苹果官方的文档,供参考
Retain Cycles: Why is that such a bad thing?
Cycles aren't bad, but they are often avoided because they can make it tricky to ensure you haven't got memory leaks. Leaks occur especially when objects are 'reference counted'. In a language or system that uses reference counting, an object keeps track of the number of references pointing at it. Every time a reference is deleted, the count goes down, when the count gets to zero, there are no references and so the object can be deleted.
This usually takes care of itself and works ok without any careful thinking. If you've got a group of objects with no cycles and you drop your reference to the root object, then it will be deleted, this means references it has to objects it owns will be dropped, the objects being referenced will have their reference counts go to zero. They'll be deleted and the cascade will cause all objects to be deleted.
But... if you have a cycle, this cascade doesn't work. You may have a group of objects and you don't want them any more, so you drop the only reference you have to these objects, but because there is a cycle the objects reference each other. This means their reference counts never go to zero, and they don't get deleted. This is a memory leak.
Clearly, you can do some careful management and break the cycles before you drop your reference to a group of objects you don't want any more. But... as I just said, this takes careful management. It's very easy to get wrong. This is one of the main reasons that memory leaks occur.
To avoid the risk of leaks and the tricky job of breaking cycles correctly when you no longer need a group of objects, programmers usually try to avoid cycles. This becomes more important on big projects with many programmers where no one person understands the whole system. If there were cycles, the programmers would have to watch out and spend a long time studying each others code to avoid cycles.
Some languages with garbage collectors (eg C#) can delete a group of objects that are no longer needed even if the group contains cycles.
The way to break a retain loop is to have a separate "close" method.
i.e.
A retains B
B retains A
when you're done, call a method (I'll call it "close") on A where A releases B. You can then release A and the whole loop will release (assuming there are no retains elsewhere).
参考地址:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html#//apple_ref/doc/uid/TP40011210-CH5-SW1
As you’ve already seen, memory for Objective-C objects is allocated dynamically (on the heap), which means you need to use pointers to keep track of an object’s address. Unlike scalar values, it’s not always possible to determine an object’s lifetime by the scope of one pointer variable. Instead, an object must be kept active in memory for as long as it is needed by other objects.
Rather than trying to worry about managing the lifecycle of each object manually, you should instead think about the relationships between objects.
In the case of an XYZPerson
object, for example, the two string properties for firstName
and lastName
are effectively “owned” by the XYZPerson
instance. This means they should stay in memory as long as the XYZPerson
object stays in memory.
When one object relies on other objects in this way, effectively taking ownership of those other objects, the first object is said to have strong references to the other objects. In Objective-C, an object is kept alive as long as it has at least one strong reference to it from another object. The relationships between the XYZPerson
instance and the two NSString
objects is shown in Figure 3-2.
When an XYZPerson
object is deallocated from memory, the two string objects will also be deallocated, assuming there aren’t any other strong references left to them.
To add a little more complexity to this example, consider the object graph for an application like that shown in Figure 3-3.
The Name Badge Maker applicationWhen the user clicks the Update button, the badge preview is updated with the relevant name information.
The first time a person’s details are entered and the update button clicked, the simplified object graph might look like Figure 3-4.
Simplified object graph for initial XYZPerson creationWhen the user modifies the person’s first name, the object graph changes to look like Figure 3-5.
Simplified object graph while changing the person’s first name The badge display view maintains a strong relationship to the original @"John"
string object, even though the XYZPerson
object now has a differentfirstName
. This means the @"John"
object stays in memory, used by the badge view to print the name.
Once the user clicks the Update button a second time, the badge view is told to update its internal properties to match the person object, so the object graph looks like Figure 3-6.
Simplified object graph after updating the badge view At this point, the original @"John"
object no longer has any strong references to it, so it is removed from memory.
By default, both Objective-C properties and variables maintain strong references to their objects. This is fine for many situations, but it does cause a potential problem with strong reference cycles.
Avoid Strong Reference Cycles
Although strong references work well for one-way relationships between objects, you need to be careful when working with groups of interconnected objects. If a group of objects is connected by a circle of strong relationships, they keep each other alive even if there are no strong references from outside the group.
One obvious example of a potential reference cycle exists between a table view object (UITableView
for iOS and NSTableView
for OS X) and its delegate. In order for a generic table view class to be useful in multiple situations, it delegates some decisions to external objects. This means it relies on another object to decide what content it displays, or what to do if the user interacts with a specific entry in the table view.
A common scenario is that the table view has a reference to its delegate and the delegate has a reference back to the table view, as shown in Figure 3-7.
Strong references between a table view and its delegateA problem occurs if the other objects give up their strong relationships to the table view and delegate, as shown in Figure 3-8.
A strong reference cycleEven though there is no need for the objects to be kept in memory—there are no strong relationships to the table view or delegate other than the relationships between the two objects—the two remaining strong relationships keep the two objects alive. This is known as a strong reference cycle.
The way to solve this problem is to substitute one of the strong references for a weak reference. A weak reference does not imply ownership or responsibility between two objects, and does not keep an object alive.
If the table view is modified to use a weak relationship to its delegate (which is how UITableView
and NSTableView
solve this problem), the initial object graph now looks like Figure 3-9.
When the other objects in the graph give up their strong relationships to the table view and delegate this time, there are no strong references left to the delegate object, as shown in Figure 3-10.
Avoiding a strong reference cycleThis means that the delegate object will be deallocated, thereby releasing the strong reference on the table view, as shown in Figure 3-11.
Deallocating the delegateOnce the delegate is deallocated, there are no longer any strong references to the table view, so it too is deallocated.
Use Strong and Weak Declarations to Manage Ownership
By default, object properties declared like this:
@property id delegate; |
use strong references for their synthesized instance variables. To declare a weak reference, add an attribute to the property, like this:
@property (weak) id delegate; |
Note: The opposite to weak
is strong
. There’s no need to specify the strong
attribute explicitly, because it is the default.
Local variables (and non-property instance variables) also maintain strong references to objects by default. This means that the following code will work exactly as you expect:
NSDate *originalDate = self.lastModificationDate; |
self.lastModificationDate = [NSDate date]; |
NSLog(@"Last modification date changed from %@ to %@", |
originalDate, self.lastModificationDate); |
In this example, the local variable originalDate
maintains a strong reference to the initial lastModificationDate
object. When thelastModificationDate
property is changed, the property no longer keeps a strong reference to the original date, but that date is still kept alive by theoriginalDate
strong variable.
Note: A variable maintains a strong reference to an object only as long as that variable is in scope, or until it is reassigned to another object or nil
.
If you don’t want a variable to maintain a strong reference, you can declare it as __weak
, like this:
NSObject * __weak weakVariable; |
Because a weak reference doesn’t keep an object alive, it’s possible for the referenced object to be deallocated while the reference is still in use. To avoid a dangerous dangling pointer to the memory originally occupied by the now deallocated object, a weak reference is automatically set to nil
when its object is deallocated.
This means that if you use a weak variable in the previous date example:
NSDate * __weak originalDate = self.lastModificationDate; |
self.lastModificationDate = [NSDate date]; |
the originalDate
variable may potentially be set to nil
. When self.lastModificationDate
is reassigned, the property no longer maintains a strong reference to the original date. If there are no other strong references to it, the original date will be deallocated and originalDate
set to nil
.
Weak variables can be a source of confusion, particularly in code like this:
NSObject * __weak someObject = [[NSObject alloc] init]; |
In this example, the newly allocated object has no strong references to it, so it is immediately deallocated and someObject
is set to nil
.
Note: The opposite to __weak
is __strong
. Again, you don’t need to specify __strong
explicitly, because it is the default.
It’s also important to consider the implications of a method that needs to access a weak property several times, like this:
- (void)someMethod { |
[self.weakProperty doSomething]; |
... |
[self.weakProperty doSomethingElse]; |
} |
In situations like this, you might want to cache the weak property in a strong variable to ensure that it is kept in memory as long as you need to use it:
- (void)someMethod { |
NSObject *cachedObject = self.weakProperty; |
[cachedObject doSomething]; |
... |
[cachedObject doSomethingElse]; |
} |
In this example, the cachedObject
variable maintains a strong reference to the original weak property value so that it can’t be deallocated as long ascachedObject
is still in scope (and hasn’t been reassigned another value).
It’s particularly important to keep this in mind if you need to make sure a weak property is not nil
before using it. It’s not enough just to test it, like this:
if (self.someWeakProperty) { |
[someObject doSomethingImportantWith:self.someWeakProperty]; |
} |
because in a multi-threaded application, the property may be deallocated between the test and the method call, rendering the test useless. Instead, you need to declare a strong local variable to cache the value, like this:
NSObject *cachedObject = self.someWeakProperty; // 1 |
if (cachedObject) { // 2 |
[someObject doSomethingImportantWith:cachedObject]; // 3 |
} // 4 |
cachedObject = nil; // 5 |
In this example, the strong reference is created in line 1, meaning that the object is guaranteed to be alive for the test and method call. In line 5,cachedObject
is set to nil
, thereby giving up the strong reference. If the original object has no other strong references to it at this point, it will be deallocated and someWeakProperty
will be set to nil
.
Use Unsafe Unretained References for Some Classes
There are a few classes in Cocoa and Cocoa Touch that don’t yet support weak references, which means you can’t declare a weak property or weak local variable to keep track of them. These classes include NSTextView
, NSFont
and NSColorSpace
; for the full list, see Transitioning to ARC Release Notes.
If you need to use a weak reference to one of these classes, you must use an unsafe reference. For a property, this means using the unsafe_unretained
attribute:
@property (unsafe_unretained) NSObject *unsafeProperty; |
For variables, you need to use __unsafe_unretained
:
NSObject * __unsafe_unretained unsafeReference; |
An unsafe reference is similar to a weak reference in that it doesn’t keep its related object alive, but it won’t be set to nil
if the destination object is deallocated. This means that you’ll be left with a dangling pointer to the memory originally occupied by the now deallocated object, hence the term “unsafe.” Sending a message to a dangling pointer will result in a crash.
Copy Properties Maintain Their Own Copies
In some circumstances, an object may wish to keep its own copy of any objects that are set for its properties.
As an example, the class interface for the XYZBadgeView
class shown earlier in Figure 3-4 might look like this:
@interface XYZBadgeView : NSView |
@property NSString *firstName; |
@property NSString *lastName; |
@end |
Two NSString
properties are declared, which both maintain implicit strong references to their objects.
Consider what happens if another object creates a string to set as one of the badge view’s properties, like this:
NSMutableString *nameString = [NSMutableString stringWithString:@"John"]; |
self.badgeView.firstName = nameString; |
This is perfectly valid, because NSMutableString
is a subclass of NSString
. Although the badge view thinks it’s dealing with an NSString
instance, it’s actually dealing with an NSMutableString
.
This means that the string can change:
[nameString appendString:@"ny"]; |
In this case, although the name was “John” at the time it was originally set for the badge view’s firstName
property, it’s now “Johnny” because the mutable string was changed.
You might choose that the badge view should maintain its own copies of any strings set for its firstName
and lastName
properties, so that it effectively captures the strings at the time that the properties are set. By adding a copy
attribute to the two property declarations:
@interface XYZBadgeView : NSView |
@property (copy) NSString *firstName; |
@property (copy) NSString *lastName; |
@end |
the view now maintains its own copies of the two strings. Even if a mutable string is set and subsequently changed, the badge view captures whatever value it has at the time it is set. For example:
NSMutableString *nameString = [NSMutableString stringWithString:@"John"]; |
self.badgeView.firstName = nameString; |
[nameString appendString:@"ny"]; |
This time, the firstName
held by the badge view will be an unaffected copy of the original “John” string.
The copy
attribute means that the property will use a strong reference, because it must hold on to the new object it creates.
Note: Any object that you wish to set for a copy
property must support NSCopying
, which means that it should conform to the NSCopying
protocol.
Protocols are described in “Protocols Define Messaging Contracts.” For more information on NSCopying
, see NSCopying
or the Advanced Memory Management Programming Guide.
If you need to set a copy
property’s instance variable directly, for example in an initializer method, don’t forget to set a copy of the original object:
- (id)initWithSomeOriginalString:(NSString *)aString { |
self = [super init]; |
if (self) { |
_instanceVariableForCopyProperty = [aString copy]; |
} |
return self; |
} |