关于循环强引用(Strong Reference Cycles)

循环引用,这里主要指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?

http://stackoverflow.com/questions/791322/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

Manage the Object Graph through Ownership and Responsibility

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 XYZPersoninstance. 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.

Figure 3-2  Strong Relationships

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.

Figure 3-3  The Name Badge Maker application

When 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.

Figure 3-4  Simplified object graph for initial XYZPerson creation

When the user modifies the person’s first name, the object graph changes to look like Figure 3-5.

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.

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.

Figure 3-7  Strong references between a table view and its delegate

A problem occurs if the other objects give up their strong relationships to the table view and delegate, as shown in Figure 3-8.

Figure 3-8  A strong reference cycle

Even 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.

Figure 3-9  The correct relationship between a table view and its delegate

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.

Figure 3-10  Avoiding a strong reference cycle

This means that the delegate object will be deallocated, thereby releasing the strong reference on the table view, as shown in Figure 3-11.

Figure 3-11  Deallocating the delegate

Once 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 NSTextViewNSFont 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_unretainedattribute:

@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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值