简体   繁体   中英

having retain cycles in code when its not causing memory leak

#import "ViewController.h"

@implementation A

- (instancetype)init
{
    self = [super init];

    if (self)
    {
        self.databaseQueue = dispatch_queue_create("someQueue", DISPATCH_QUEUE_SERIAL);
    }

    return self;
}
- (void) privateLogMethod
{
    NSLog(@"private log method called");
    [NSThread sleepForTimeInterval:1];
}

- (void) performSomeAction
{
    for (NSInteger i = 0; i < 10; i++) {
        dispatch_async(_databaseQueue, ^{
            NSLog(@"inside for loop");
            [self privateLogMethod];
        });
    }
}

- (void) dealloc
{
    NSLog(@"removing A from memory");
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.someClassA = [[A alloc] init];

    [self.someClassA performSomeAction];

    [self performSelector:@selector(removeA) withObject:nil afterDelay:5];
}

- (void) removeA
{
    NSLog(@"call to remove A from memory");
    self.someClassA = nil;
}

@end


#import <UIKit/UIKit.h>

@interface A: NSObject
- (void) performSomeAction;
@property (nonatomic, strong) dispatch_queue_t databaseQueue;
@end

@interface ViewController : UIViewController
@property (nonatomic, strong) A* someClassA;
@end

If we see the code above, there is a retain cycle since class A is holding on to databaseQueue and databaseQueue is holding on to self . Thus, when ViewController asks to dealloc A after 5 seconds, class A holds on to finish the loop before deallocating itself. Below is the output.

2017-09-14 14:21:06.774517+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:06.774768+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:07.775218+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:07.775480+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:08.778805+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:08.779251+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:09.784467+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:09.785089+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:10.790469+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:10.790929+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:11.775522+0530 testdealloc[72021:1812575] **call to remove A from memory**
2017-09-14 14:21:11.796196+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:11.796659+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:12.802018+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:12.802483+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:13.804953+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:13.805432+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:14.806252+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:14.806604+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:15.807852+0530 testdealloc[72021:1812626] inside for loop
2017-09-14 14:21:15.808306+0530 testdealloc[72021:1812626] private log method called
2017-09-14 14:21:16.809550+0530 testdealloc[72021:1812626] **removing A from memory**

My question is this: We've a retain cycle in the code, however, this does not cause memory leak. Is it okay, in such scenarios, to leave the code as is [because it does not cause memory leak for sure]? or should I use __weak weakSelf = self and then weakSelf in the block to ensure there is no retain cycle at all?

You ask:

Is this okay?

Largely, yes.

More accurately, it depends upon your needs/intent. If you need this to continue to run for your app to function properly (eg maybe you're converting and saving some images), then you definitely want a strong reference so that it finishes).

But if you don't need to continue to execute, then you'd use weakSelf pattern. For example, imagine you had a series of dispatched tasks in a view controller that were there solely to later trigger UI updates. In that case, if the view controller was dismissed, you might not need these blocks to run, and you certainly wouldn't want it to hang on to the view controller for a view that was long since dismissed. (You might even want to cancel those dispatched items when the view controller was dismissed, but that's another topic.)

Bottom line, it depends upon your intent.

I second @Rob. But look for any single opportunity to avoid retain cycles. This is okay for a simple project, but while working on complex projects... it will save lots of time trying to debug/resolve an issue where you are not certain about the root cause.

Here is a beautiful article explaining different types of retain cycles and to avoid them.

https://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM