coconata
coconata

Reputation: 187

Retain cycles with a singleton in Objective-C

I have a singleton class named MyManager. I use self inside of its blocks: requestSomeInfo and requestSomeInfoWithString. And requestSomeInfoWithString method gets self.string as a parameter. Does this code lead to retain cycles in both the blocks?

The project is with ARC.

@interface MyManager : NSObject

+ (instancetype)sharedInstance;
- (void)requestSomeInfo:(void (^)(BOOL success))completion;
- (void)requestSomeInfoWithString:(NSString *)string completion:(void (^)(BOOL success))completion;

@end

@implementation MyManager

+ (instancetype)sharedInstance {
    static MyManager *shared = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shared = [MyManager new];
    });
    return shared;
}

@end


@interface MyClass

@property string;

@end

@implementation MyClass
.....

[[MyManager sharedInstance] requestSomeInfo:^(BOOL success) {
if (success) {
    [self someAction];
}
}];

[[MyManager sharedInstance] requestSomeInfoWithString:self.string completion:^(BOOL success) {
    if (success) {
        [self someAction];
    }
}];

.....
@end

Upvotes: 2

Views: 168

Answers (1)

CRD
CRD

Reputation: 53000

Your description does not mention MyClass but your code fragment does. Going by the code fragment you have methods in MyClass which makes calls to methods in MyManager. These calls directly pass blocks as arguments, without themselves storing references in instance variables to those blocks; you do not show whether MyManager stores references to the blocks it receives in its instance variables but it is reasonable to assume that it does.

The blocks that are passed use self, which references the instance of MyClass that created the block.

So from the point within the MyManager methods where they store the revived block references into instance variables you have the following chain of references:

shared -> your MyManager instance

the MyManager instance -> the passed in block

the passed block -> references the MyClass instance which created it

That's not a reference cycle, so unless you have more inter-object references in your real code you don't have a cycle.

A reference cycle is not itself bad, they can get created during the course of normal prgogram execution and sometimes might be vital to keep the appropriate objects alive; a cycle may be temporary/of finite lifetime after which at least one of the links in the cycle is broken. A reference cycle only becomes the bad retain cycle when they unintentionally result in object instances living beyond their required lifetime...

Which finally brings us to your code. You have a shared instance of MyManager which probably lives from its point of creation until your program finishes. In that instance you Store references to blocks, and those blocks Store references to MyClass instances... This is somewhere where you might have an inadvertent "leak"; if your MyManager methods do not erase (store nil into) the instance variables that reference those blocks once those references are no longer required (probably immediately after the referenced block is invoked) then you could have long term unintentional references to MyClass instances that have long been otherwise discarded. Erase those block references once used and the possibility vanishes.

Hope that all makes sense!

Upvotes: 2

Related Questions