I am new to retain cycle which I am confused if my situation falls into.
I have a singleton class
Singleton.h
#interface Singleton : NSObject
+ (Singleton *)sharedInstance;
- (void)doSomethingWithData:(NSDictionary *)data untilDoneReturnToTarget:(id)target selector:(SEL)selector;
#end
Singleton.m
#implementation RequestSingleton
static Singleton *shared = nil;
#pragma mark - System
- (id)init {
self = [super init];
if (self) {
}
return self;
}
#pragma mark - Interface
+ (Singleton *)sharedInstance {
static dispatch_once_t pred;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
});
return shared;
}
- (void)doSomethingWithData:(NSDictionary *)data untilDoneReturnToTarget:(id)target selector:(SEL)selector {
[someClass doSomething:data
completionHandler: ^{
if ([target respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:selector
withObject:someObject]; // is this a retain-cycle?
#pragma clang diagnostic pop
}
}];
}
Now in a view controller, I call doSomethingWithData:untilDoneReturnToTarget:selector: defined in Singleton class:
- (void)function {
Singleton *singleton = [Singleton sharedInstance]
[singleton doSomethingWithData:someData
target:self
selector:#selector(processResult:)];
}
- (void)processResult:(id)data {
…
}
I'm not sure if my implementation in doSomethingWithData:untilDoneReturnToTarget:selector: has fallen into retain-cycle? Should I use weakTarget instead of target in the block implementation? Please help me clarify.
Thanks in advance.
There is no retain cycle in your code as far as I can see. But you are extending target's lifecycle until you finish processing data.
If you want to keep the same behaviour but issue a callback only if target is still around, use weak reference
Here is a good article that might help http://digitalleaves.com/blog/2015/05/demystifying-retain-cycles-in-arc/
Update: one of the most common cases for retain cycle is mutual strong reference: object → block and block → object. Sometimes you can even introduce it not knowingly by using API that would internally create a retain cycle if you are not careful, e.g. ReactiveCocoa (that's why they even have macros to fight it called weakify and strongify).
If you have a singleton, it won't get deallocated anyway, even if there's no retain cycle.
Related
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?
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:
self is retained by the block
The block is retained by an internal subscriber object
The subscriber is retained by some internal thing of the RACObserve signal until the signal terminates.
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.
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);
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.
I would like to take the GCD approach of using shared instances to the next step so I created the following code:
#implementation MyClass
static id sharedInstance;
#pragma mark Initialization
+ (instancetype)sharedInstance {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
if (sharedInstance) {
return sharedInstance;
}
#synchronized(self) {
self = [super init];
if (self) {
sharedInstance = self;
}
return self;
}
}
#end
I assume the sharedInstance method seems to be ok but I am unsure about the init method. The reason for creating this is that I don't want people using my SDK, to use the init method, and if they do ... make it bullet proof.
Instead of transparently redirecting calls to init to the singleton implementation which can cause very confusing behaviour for the users of your SDK, I suggest not allowing to call init at all:
+ (instancetype)sharedInstance {
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
- (instancetype)init {
#throw [NSException exceptionWithName:NSInternalInconsistencyException reason:#"..." userInfo:nil];
}
- (instancetype)initPrivate {
if (self = [super init]) {
...
}
return self;
}
I would like to suggest new ways of solving your problem.
You can use NS_UNAVAILABLE in the header file just like this:
//Header file
#interface MyClass : NSObject
+ (instancetype)sharedInstance
- (instancetype)init NS_UNAVAILABLE;
//...
#end
In this case init function will not be available from outside, will not be suggested for autocompletion, and you'll be able to normally use the init method inside implementation file.
As you are making a singleton class I would suggest you to make new method unavailable too by adding this line to the header file:
+ (instancetype)new NS_UNAVAILABLE;
There is also an old way of making methods unavailable (which can be used in header too):
- (instancetype) init __attribute__((unavailable("Use 'sharedInstance' instead of 'init' as this class is singleton.")));
This can be used if you want to prompt some message about unavailability.
The general opinion is that trying to protect your singleton against that kind of bug is pointless. Whoever calls [[LUIMain alloc] init] and creates a singleton gets what they deserved.
And the code that you wrote isn't thread safe anyway. If I call [[LUIMain alloc] init] while someone else calls sharedInstance, sharedInstance will return a different object than on the next call. (#synchronized (self) in the init method is pointless, because a second caller will have a different self).
I'm trying to add some data from a random class to my viewController,
So to keep always the same data, i did a singleton on my UIViewController, but it doesnt work i never get the data on my tableview.
this what i added to my UIViewController :
+(id)sharedMBVC {
static MBViewController *sharedMBVC ;
#synchronized(self) {
if (!sharedMBVC)
sharedMBVC = [[MBViewController alloc] init];
return sharedMBVC;
}
}
and from my class i call it by doing this :
MBViewController *vc = [MBViewController sharedMBVC];
Do i have to set somewhere the content of my NSArrays that they are declared in my viewDidLoad of the viewcontroller ? or there is something else to do.
PS : i was doing in my class before vc = (MBViewController *)[[[[UIApplication sharedApplication] delegate] window] rootViewController]; but now my uiviewcontroller its not a rootview anymore, thats why im trying to find other way to access to it, and i guess the best solution is to do a singleton
Can u help me guys
OK, so the problem you have is that the viewController that displays the arrays also "owns" the arrays. This means that (with your current setup) to be able to change the arrays you need to get hold of the viewController to be able to access the arrays.
You need to change this by removing the arrays from that viewController.
You can still do this with a singleton (if you prefer) but create a brand new class called something like ArrayManager.
This will contain the arrays and ALL the methods for updating the arrays.
So for instance if your viewController has a method called - (void)addObjectToArray:(id)object; then move this method to the ArrayManager singleton class.
Now in your displaying viewController you can do...
[[ArrayManager sharedInstance] getSomeDataFromTheArray];
And in the place that has to update the array you can do...
[[ArrayManager sharedInstance] addObjectToArray:someObject];
Now you don't need to worry about passing the viewController around at all.
This can be improved with various things. For instance, you maybe don't need a singleton at all and can just take this ArrayManager class and inject it into the places that need it by setting a property etc...
Also, you could possibly use CoreData to store the information.
Also, your singleton method is not correct. The way recommended by Apple is to use...
+ (ArrayManager *)sharedInstance
{
static dispatch_once_t once;
static ArrayManager *arrayManager;
dispatch_once(&once, ^{
arrayManager = [[ArrayManager alloc] init];
});
return arrayManager;
}
Rewriting your singleton...
.h file
#interface PTVData : NSObject
+ (PTVData *)sharedInstance;
- (void)addSensor:(NSString *)sensorName;
- (NSInteger)numberOfSensors;
- (NSString *)sensorAtIndex:(NSUInteger)index;
#end
.m file
#interface PTVData ()
#property (nonatomic, strong) NSMutableArray *sensors;
#end
#implementation PTVData
+ (PTVData)sharedInstance
{
static PTVData *sharedPTVData;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPTVData = [[PTVData alloc] init];
});
return sharedPTVData;
}
- (id)init
{
if (self = [super init]) {
_sensors = [[NSMutableArray alloc] initWithObject:#"None"];
}
return self;
}
- (void)addSensor:(NSString *)sensorName
{
if (sensorName
&& ![self.sensors containsObject:sensorName]) {
[self.sensors addObject:sensorName];
}
}
- (NSInteger)numberOfSensors
{
return self.sensors.count;
}
- (NSString *)sensorAtIndex:(NSUInteger)index
{
return self.sensors[index];
}
#end
By doing this you hide the actual array of sensors. It is only directly accessible through the PTVData class.
Now in your tableview methods you can do...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[PTVData sharedInstance] numberOfSensors];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...
cell.textLabel.text = [[PTVData sharedInstance] sensorAtIndex:indexPath.row];
return cell;
}
Well, I think, these arrays don't belong to the MBViewController from architectural point of view. I would separate them to a data layer (DataSource class of some sort, for instance) and keep a reference to the DataSource everywhere you need. Have a look at the Second iOS App tutorial by Apple. It contains a simple example of data layer implementation.
UPDATE:
Also, check out Fogmeister's answer. He explains a possible implementation of such object rather well :)
As for why singleton didn't work as you expected in this case, I believe, the reason could be the following:
If you get to the MBViewController via a segue (which, I think, you are), then a new instance of MBViewController is created every time. If you access your arrays from MBViewController using self.myArray, then you access this new MBViewController's myArray. While sharedMBVC keeps a reference to the shared instance, it's just ignored by the segue.
in my ViewController.h
#property PTVData *ptvdata;
ViewController.m
ViewDidload
ptvdata = [PTVData sharedPTVData];
_sensorsCollection = ptvdata.sensorsCollection;
then i have a method in my ViewController.m
- (void) addSensorToCollection:( NSString *)sensorName{
[[PTVData sharedPTVData] addSensorToCollection:sensorName];
_sensorsCollection = ptvdata.sensorsCollection;
[ self.tableView reloadData];
}
}
My PTVData.h
#property (nonatomic,retain) NSMutableArray *sensorsCollection;
+(id)sharedPTVData;
-(id) init;
- (void) addSensorToCollection:( NSString *)sensorName;
#end
my PTVData.m
#synthesize sensorsCollection = _sensorsCollection;
+ (id)sharedPTVData {
static PTVData *sharedPTVData = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPTVData = [[self alloc] init];
});
return sharedPTVData;
}
- (id)init {
if (self = [super init]) {
_sensorsCollection = [[NSMutableArray alloc]initWithObjects:#"None", nil];
}
return self;
}
- (void) addSensorToCollection:( NSString *)sensorName{
if (![_sensorsCollection containsObject:sensorName]&& sensorName!= nil) {
[_sensorsCollection addObject:sensorName];
}
}
Instead of initializing your arrays in viewDidLoad, do it in sharedMBVC function. This will ensure that arrays are not re-initialized every time the view loads.
I have one NSMutableArray in FirstViewController declared as firstArray.
I want to copy the secondArray into firstArray.
In the SecondViewController,
Self.FirstViewController.firstArray = self.secondArray;
When I attempt to NSLog the firstArray.count from the FirstViewController, it display 0. It should have two objects in the array
Anyone can advise on this?
You can choose one of this solutions:
Singleton
Passing Data between ViewControllers
Delegation
You can find all the info you need right here: https://stackoverflow.com/a/9736559/1578927
Singleton example:
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
It looks like either the second array has already been deallocated when passing the reference to the first view controller, or the first view controller itself has already been nilled out. If the first is true, then you may need a different model object to hold your data rather than persisting it in the controller layer of your app. If that is not the case, then you may want to consider a direct copy. The easiest way of doing this is to declare the firstArray property as the keyword copy rather than strong in your interface file.
If you do need to persist the data in the model layer of your app, a singleton pattern object would indeed be one way of achieving this as EXEC_BAD_ACCESS (nice name!) points out. A slightly more modern (though functionally equivalent) way of writing a singleton is as follows.
#interface MySingleton : NSObject
#property (strong, readwrite) id myData;
+ (id)sharedSingleton
#end
#implementation MySingleton
+ (id)sharedSingleton
{
static MySingleton *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[MySingleton alloc] init];
// Do other setup code here.
});
return singleton;
}
#end
Note the use of dispatch_once - this makes certain that the static singleton can only be created once (whereas technically, you can invoke +[NSObject initialize] as many times as you feel like manually, though I'd never advise doing so).
You may also take advantage of NSNotificationCenter
SecondViewController.m
[[NSNotificationCenter defaultCenter] postNotificationName:#"arrayFromSecondVC" object:secondArray];
FirstViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(populateArray:) name:#"arrayFromSecondVC" object:nil];
}
-(void)populateArray:(NSNotification *)notif
{
self.firstArray = [notif object];
}
And remove the notification when the viewUnload or didRecieveMemoryWarning method.
Hope it helps.
I have an object that is being retained more than necessary (most likely due to a property that is strong instead of weak). Big codebase, so it's hard to find where.
How can I find all the lines in which this object is being retained when using ARC?
If I weren't using ARC I guess I could simply override retain and check from where it's called. Can I do something similar with ARC?
To track growth of an application, Heapshot Analysis has proven very effective. It will capture both true leaks and accretion of memory where the allocations are not accounted for by leaks.
You can see all of the retain/release events, and their backtrace, using the Allocations instrument. Hit the little (i) button on the Allocations instrument and turn on "Record reference counts". Turning on "Only track active allocations" reduces the amount of data collected by Instruments, making it snappier (and dead allocations aren't really useful in this context, but can be in others).
With that, you can dive into any allocation (by clicking on the right-arrow in the address field), see all the retain/release events and see exactly where they occurred.
I managed to find the offending retain by doing the following:
Temporarily add -fno-objc-arc to the object class Compiler Flags
to disable ARC for that class.
Temporarily override retain (just call super) and put a breakpoint on it.
Debug and check the call stack each time retain is called.
Last week I was helping some friends debug leaks in their ARC project.
Some tips:
1/ Build for Profiling and start Instruments with Leak Detection. Then explore the currently allocated objects, find the object you want (you can sort them by name) and look into its retain/release history. Note that retain count is not very helpful with ARC. You have to check it manually step by step.
Try to comment all the code which could be the source of leak and then uncomment it step by step.
2/ Put a NSLog into your init and into your dealloc to watch when the object is created and destroyed.
3/ Don't look only to property definitions, watch if property setters are implemented manually. I found a problem in my friends' project looking like this:
#property (weak, nonatomic) id<...> delegate;
#interface ... {
id<...> _delegate;
}
#synthesize delegate = _delegate;
- (void)setDelegate(id<...>)delegate {
_delegate = delegate; //with ARC this retains the object!
}
This solution was somewhat helpful for me. It basically uses method swizzling to tricks the ARC compiler into thinking you're not overriding retain and release.
#import <objc/runtime.h>
...
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
SEL originalSelector1 = NSSelectorFromString(#"retain");
SEL swizzledSelector1 = NSSelectorFromString(#"myretain");
SEL originalSelector2 = NSSelectorFromString(#"release");
SEL swizzledSelector2 = NSSelectorFromString(#"myrelease");
Method originalMethod1 = class_getInstanceMethod(cls, originalSelector1);
Method swizzledMethod1 = class_getInstanceMethod(cls, swizzledSelector1);
Method originalMethod2 = class_getInstanceMethod(cls, originalSelector2);
Method swizzledMethod2 = class_getInstanceMethod(cls, swizzledSelector2);
BOOL didAddMethod1 =
class_addMethod(cls,
originalSelector1,
method_getImplementation(swizzledMethod1),
method_getTypeEncoding(swizzledMethod1));
if (didAddMethod1) {
class_replaceMethod(cls,
swizzledSelector1,
method_getImplementation(originalMethod1),
method_getTypeEncoding(originalMethod1));
} else {
method_exchangeImplementations(originalMethod1, swizzledMethod1);
}
BOOL didAddMethod2 =
class_addMethod(cls,
originalSelector2,
method_getImplementation(swizzledMethod2),
method_getTypeEncoding(swizzledMethod2));
if (didAddMethod2) {
class_replaceMethod(cls,
swizzledSelector2,
method_getImplementation(originalMethod2),
method_getTypeEncoding(originalMethod2));
} else {
method_exchangeImplementations(originalMethod2, swizzledMethod2);
}
});
}
-(id)myretain {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSLog(#"tracking retain now %#",#((uintptr_t)[self performSelector:NSSelectorFromString(#"retainCount")]));
SEL selector = NSSelectorFromString(#"myretain");
return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}
-(id)myrelease {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSLog(#"tracking release now %#", #((uintptr_t)[self performSelector:NSSelectorFromString(#"retainCount")]));
SEL selector = NSSelectorFromString(#"myrelease");
return [self performSelector:selector withObject:nil];
#pragma clang diagnostic pop
}
If you are using ARC you would never get the option to add retain,
and if you have converted the project to ARC using below option, you would be prompted with error
If you have set the property as strong, then you should allocate the object once through the whole project e.g. self.yourobject = [[NSMutableArray alloc]init];. There is no shortcut for this.