air_bob
air_bob

Reputation: 1337

why this RACObserve block caused retain cycle?

Consider I in my view controller, I added RACObserve of property of Singleton, and inside subscribNext I have a self reference in it. The code is as below:

[RACObserve([Singleton shared], singletonFlag) subscribeNext:^(NSNumber *singletonFlag) {
        self.flag = [singletonFlag boolValue];
    }];

Based on my understanding, self don't hold a strong reference of the block(while block hold a strong reference of self), this shouldn't cause retain cycle. I have read memory management of reactive cocoa as well https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/Legacy/MemoryManagement.md In which they provide an example as

[RACObserve(self, username) subscribeNext:^(NSString *username) {
    [self validateUsername];
}];

I totally understand why it caused the retain cycle in above case and we need a weak self inside the block. I am confused why in the first case, it will cause a retain cycle. To confirm this, just paste that code snippet after viewDidLoad and see whether the view controller was dealloc-ed when it should be. If you need see more implementations of the singleton, this is the code,

@interface Singleton : NSObject
@property (readwrite,nonatomic) BOOL singletonFlag;
@end

@implementation Singleton
+ (Singleton *)shared {
    static dispatch_once_t pred = 0;
    __strong static id _sharedObject = nil;
    dispatch_once(&pred, ^{
        _sharedObject = [[self alloc] init];
    });
    return _sharedObject;
}

- (id)init {
    if (self = [super init]) {
        NSLog(@"init of %@",NSStringFromClass(self.class));
    }
    return self;
}
@end

Anyone enlighten me about this?

Upvotes: 1

Views: 863

Answers (3)

The problem is that RACObserve() will return you a RACDisposable object, that you have to dispose your self. If you use it the way RAC()=RACObserve(), then the RAC() part will take care of killing the RACDisposable object that is returned by RACObserve() method.

One quick fix that you can make when using the RACObserver like this:

[RACObserve(self, username) subscribeNext:^(NSString *username) {
[self validateUsername];

}];

Is to turn it into this: (RACDisposable *disposableSignal; declared in .h for example)

disposableSignal=[RACObserve(self, username) subscribeNext:^(NSString *username) {
[self validateUsername];

}];

And use [disposableSignal dispose] to deallocate the signal. For example in viewWillDisappear method. Basically you have to kill it with dispose method to get rid of it.

Upvotes: 0

KPM
KPM

Reputation: 10608

Anyway, you shouldn't write such things as

[RACObserve([Singleton shared], singletonFlag) subscribeNext:^(NSNumber *singletonFlag) {
  self.flag = [singletonFlag boolValue];
}];

Instead, write

RAC(self, flag) = RACObserve([Singleton shared], singletonFlag);

Upvotes: 0

Cosyn
Cosyn

Reputation: 4987

The internal implementation is quite complicated, It's not important whether there is a real retain cycle.

Here the reason why memory leaks is just the same in your two examples:

  1. self is retained by the block
  2. The block is retained by an internal subscriber object
  3. The subscriber is retained by some internal thing of the RACObserve signal until the signal terminates.
  4. The RACObserve signal terminates when either the target (the singleton instance) or self (the RACObserve micro is implicitly using self) is deallocated.

But now the singleton instance won't dealloc, and self won't dealloc neither since it's already retained. So the signal never terminates, then memory leaks.

Upvotes: 4

Related Questions