I am a noob with properties and how to manually release things since I typically use ARC but in this case I am not so I need a little help on how to fix the leak below. Now since it is a property, do I just stick a autorelease on the end or not. I am not sure! :)
Anyway if anyone can tell me how to properly get rid of this leak, that would be great!
- (id)init
{
self = [super init];
if (self)
{
self.purchasableObjects = [[NSMutableArray alloc] init];
self.storeObserver = [[ZTStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self.storeObserver];
}
return self;
}
This fixed both of the reported leaks:
- (id)init
{
self = [super init];
if (self)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
self.purchasableObjects = array;
[array release];
ZTStoreObserver *observer = [[ZTStoreObserver alloc] init];
self.storeObserver = observer;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self.storeObserver];
[observer release];
}
return self;
}
You shuold use strong, if you dont want to loose them.
#property(strong) NSMutableArray *purchasableObjects;
#property(strong) ZTStoreObserver *storeObserver;
I assume self.purchasableObjects must be strong reference(if it is weak, the memory will not be retained unless, there exist another strong reference to keep this object alive), Then you will not leak memory, but you may find total memory footprint increased, thats called allocation.To keep the memory footprint low, you can set that variable to nil and avoid retain cycles once you are done with that variable.You can probably forget about this variable until you receive a memory warning, at that point you can nil this object to reclaim the memory
Related
This is my code
__weak KDObject *obj = [KDObject fetchObj] ;
NSLog(#"%#", obj) ; // I think it should be nil, but it is not
obj.i = 10 ;
NSLog(#"%d", obj.i) ;
In KDObject.m
#implementation KDObject
+ (instancetype)fetchObj
{
return [[self alloc] init] ;
}
#end
the result is the same whatever KDOjbect.m is compile with -fno-objc-arc flag or without -fno-objc-arc flag
Anybody has ideas why obj is not nil ?
Related to your Q and to your answer:
-fectchObject is a method not belonging to any method family with ownership transfer. Therefore ARC has to ensure that returning the reference is safe. That means that losing the strong reference in the local scope of -fetchObject does not give up the last reference.
One way to accomplish this is to use the autorelease pool. But ARC does not guarantee that the ARP is used. Moreover it tries not to use the ARP, because it is the solution with the highest memory pressure.
So the things happening depends of the compiler implementation, attributes set to the method and what the compiler sees in source code (esp. implementation of -fetchObject). So you should not rely on returning in ARP.
__weak is guaranteed to be nil, if the object is destroyed. But it is not guaranteed that the object is destroyed in the earliest possible moment. This is subject of optimization.
From the docs about __weak
__weak specifies a reference that does not keep the referenced object alive. A weak reference is set to nil when there are no strong
references to the object.
Whether it's __weak or not KDObject *o = [[KDObject alloc] init] creates an object so o is not nil.
__weak is something realated to memory management. If none of strong objects are pointing to a weak object, it would be released from memory.
- (void)loadView
{
[super loadView];
TestObject *obj_ = [[TestObject alloc] init];
pObj = obj_;
if(pObj == nil)
{
NSLog(#"pObj_ is not nil");
}
__weak TestObject *obj2_ = [[TestObject alloc] init];
if(obj2_ == nil)
{
NSLog(#"obj2_ is nil");
}
__weak TestObject *obj3_ = [TestObject createInstance];
if(obj3_ == nil)
{
NSLog(#"obj3_ is nil");
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(pObj == nil)
{
NSLog(#"pObj is nil");
}
}
KudoCC requested this code. I used LLVM5.1. If I use -fno-objc-arc to TestObject.h, the objc3_ became not nil.
The leak instruments warn me about a memory leak related to this part of code:
[self.contview addSubview:nav.view];
Here are how I manage the view:
[nav.view removeFromSuperview];
self.nav = [[[destinationClass alloc] initWithNibName:pagename bundle:nil] autorelease];
[self.contview addSubview:nav.view];
Is it normal that the self.nav has a retainCount of 2 just after been allocated?Could this be related to the memory leak?
I'm very new to the memory management can someone give me some help?
Many Thanks
Assuming nav is a strong (retain) property, it retains the view controller you are assigning here:
self.nav = [[[destinationClass alloc] initWithNibName:pagename bundle:nil] autorelease];
effectively, the retain count after this line of code is 1; +2 for alloc and retain and -1 for autorelease. Generally you should never use retainCount method to determine the actual retain count of the object, maybe this answer will give you more insight why.
Every alloc, retain or copy call should be matched with a release or autorelease call. You should add a matching release call in dealloc method of your class
-(void) dealloc {
[_nav release];
_nav = nil;
[super dealloc];
}
Don't use manual memory management, use ARC, it will make your life much easier :)
Until yesterday I thought I understood how properties memory management works, but then I ran an "Analize" task with XCode and got plenty of "This object is not own here". Here is a simple example that describes my problem :
MyObservingObject.h:
#interface MyObservingObject : NSObject
#property(nonatomic, retain) NSMutableDictionary *observedDictionary;
-(id)initWithDictCapacity:(int)capacity;
#end
MyObservingObject.m:
#synthesize observedDictionary;
-(id)initWithDictCapacity:(int)capacity {
self = [super init];
if (self) {
self.observedDictionary = [[[NSMutableDictionary alloc] initWithCapacity:capacity] autorelease];
}
return self;
}
- (void)dealloc {
// The following line makes the Analize action say :
// "Incorrect decrement of the reference count of an object that is not owned at this point by the caller"
[self.observedDictionary release], self.observedDictionary=nil;
[super dealloc];
}
What I don't understand is Why should I leave this property without calling release on it? My #property is set as retain (copy does the same), so when I'm doing self.myRetainProperty = X, then X got its retain count increased (it's owned by self), didn't it ?
You should let the setter do the releasing for you, so remove the call to release in dealloc:
- (void)dealloc {
self.observedDictionary=nil;
[super dealloc];
}
This is because the setter will be synthensized to something like:
- (void)setObject:(id)object
{
[object retain];
[_object release];
_object = object;
}
Which will work as desired when you pass in nil.
It did get increased, but when you set it to nil, the setter method first releases the backing instance variable, and only then does it retain and assign the new value. Thus setting the property to nil is enough, setting the ivar to nil leaks memory, though.
For your better understanding: the typical implementation of an autogenerated retaining setter is equivalent to something like
- (void)setFoo:(id)foo
{
if (_foo != foo) {
[_foo release];
_foo = [foo retain];
}
}
Also note that, as a consequence, you should never release properties like this. If you do so, the backing ivar may be deallocated, and messaging it (release by the accessor when setting the property to nil afterwards) can crash.
You don't need to do
[self.observedDictionary release]
before
self.observedDictionary=nil;
This is enough, because this is a property, and it will automatically send release to previous value
self.observedDictionary=nil;
The reason for the compiler warning is because of the way you are retrieving the object.
By calling
[self.observedDictionary release];
you are in fact going through the accessor method defined as
- (NSDictionary *)observedDictionary;
This returns your object but due to the naming of observedDictionary the compiler assumes that there is no transfer of ownership e.g. the callee will not have to release this object unless they take a further retain. It is because of this that the compiler thinks you are going to do an overrelease by releasing an object that you don't actually own.
More specifically the convention for method names that transfer ownership is for them to start with copy, mutableCopy, alloc or new.
Some examples
Here I have used a name that does not imply transfer for ownership so I get a warning
- (id)object;
{
return [[NSObject alloc] init];
}
//=> Object leaked: allocated object is returned from a method whose name ('object') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa
Fix 1: (don't transfer ownership)
- (id)object;
{
return [[[NSObject alloc] init] autorelease];
}
Fix 2: (make the name more appropriate)
- (id)newObject;
{
return [[NSObject alloc] init];
}
With this knowledge we can of naming convention we can see that the below is wrong because we do not own the returned object
[self.object release]; //=> Produced warnings
And to show a final example - releasing an object that implies ownership transfer with it's name
[self.newObject release]; //=> No Warning
I've seen several different approaches to memory management in iOS as regards releasing properties. After some debate with colleagues, the pros and cons have become muddled in my head.
I'm hoping to get a summary of pros and cons that will allow myself and others to easily choose a default approach while still understanding when to make exceptions. Here are the 3 variations I've seen:
Assume #property (nonatomic, retain) MyObject *foo;
// Release-only. Seems to be the favored approach in Apple's sample code.
- (void)dealloc {
[foo release];
[super dealloc];
}
// Property accessor set to nil.
- (void)dealloc {
self.foo = nil;
[super dealloc];
}
// Release, then nil.
- (void)dealloc {
[foo release];
foo = nil;
[super dealloc];
}
If you have a different variation to add, comment here and I'll edit the op.
Versions (1): is the best. Each of the others have attributes that may be harmful.
Version (2): It is generally advised not to use accessors in dealloc (or init). The reasoning behind this is that the object is in the process of being torn-down (or created) and is in an inconsistent state. This is especially true if you are writing a library where someone else may later override an accessor unaware that it may be called when the object is in an inconsistent state. (Of course even Apple sometimes breaks this rule -[UIView initWithFrame:] calls -[UIView setFrame:] if the argument is not CGRectZero which can make for fun debugging.
Version (3); Setting the ivar to nil serves no useful purpose, indeed it may mask an error and make debugging more difficult. To see why this is true, consider the following piece of code, assume myObject has a version (3) dealloc.
FastMovingTrain* train = [[FastMoving alloc] init];
MyObject* myObject = [[MyObject alloc] init];
myObject.foo = train;
[train release];
// my myObject.foo is the only thing retaining train
...
....
[myObject release];
// Because of version (3) dealloc if myObject
// points to the dealloced memory this line
// will silently fail...
[myObject.foo applyBrakes];
Interestingly enough this code provides an opportunity to demonstrate when setting a variable to nil after a release does make sense. The code can be made more resilient by modifying it as follows.
FastMovingTrain* train = [[FastMoving alloc] init];
MyObject* myObject = [[MyObject alloc] init];
myObject.foo = train;
[train release];
// my myObject.foo is the only thing retaining train
...
....
[myObject release];
myObject = nil;
// This assertion will fail.
NSAssert(myObject, #"myObject must not be nil");
[myObject.foo applyBrakes];
Just my $0.02.
I must have misunderstood some of the memory management rules, because when I try to fix a memory leak, the App crashes. Let me show you some code:
calendarRequestLog is a property of type MutableDictionary in a singleton object, that exists as long as the App runs. Here's the declaration in the .h file:
#property (nonatomic, retain, readonly) NSMutableDictionary *calendarRequestLog;
I allocate it with (in init):
calendarRequestLog = [[NSMutableDictionary alloc] init];
I fill it with this (notice the retain, that creates the memory leak):
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
I sometimes access it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
// add delegates
}
I empty it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
for (id <ServerCallDelegate> delegate in delegates) { … }
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
}
Here's the code that crashes when I remove the retain above:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
It crashes because delegates is deallocated but not nil. To be more precise, I get an EXC_BAD_ACCESS Exception.
All these methods may be called in different orders or multiple times.
I cannot figure out, why this happens. I thought, collections are supposed to retain their objects - as this array-object (delegates) is still in the collection, it should not be deallocated. Other code cannot be responsible, I showed you all occurrences of calendarRequestLog.
I appreciate all the help I can get!
#Edit
I think I got it.
I call the crashing method when the delegate gets deallocated, so that I do not call the delegate per accident later.
But: I retain the delegates in my calendarRequestLog, so it cannot get deallocated as long as this doesn't get called:
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
...which in turn, deallocates the delegate and calls the crashing method. As the calendarRequestLog has removed the delegates, but not yet the key, we crash.
Ok, I will solve this differently. Thanks for all the comments - thanks to you, I looked elsewhere!
Did you try retaining when fetching so nobody releases your object while you're using it?
NSMutableArray* delegates = [[calendarRequestLog objectForKey:date] retain];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
[delegates release];
Common practice is the following, because you already retain in the .h file:
//create local instance, then copy that to the class wide var
NSMutableDictionary *_calendarRequestLog = [NSMutableDictionary alloc] init];
self.calendarRequestLog = _calendarRequestLog;
[_calendarRequestLog release];
Also, I don't really understand why you would retain here:
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
Why not just change that to:
[calendarRequestLog setObject:[NSMutableArray arrayWithObject:delegate] forKey:date];
Write instead
calendarRequestLog = [[NSMutableDictionary alloc] init];
this
self.calendarRequestLog = [NSMutableDictionary dictionary];
and try to use property instead ivar