1
votes

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?

3
It's not really a cycle, but the singleton never gets deallocated due to being a singleton, so the block which holds a strong reference to your view controller will never let go of that reference.dan
@dan Why not post that as an answer? :)mattsson
thanks @dan I think I get it now, which means this subscribeNext block is not like our normal defined blocks(which was dealloc after block execution), am I correct to say that?air_bob

3 Answers

4
votes

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.

0
votes

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);
0
votes

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.