I am attempting to wrap my head around one part of the Objective-C memory model (specifically on the iPhone, so no GC). My background is C/C++/Java, and I am having an issue with the following bit of code (also wondering if I am doing this in an "Objective-C way" or not):
- (NSSet *) retrieve
{
NSMutableSet *set;
set = [NSMutableSet new];
// would normally fill the set in here with some data
return ([set autorelease]);
}
- (void) test
{
NSSet *setA;
NSSet *setB;
setA = [self retrieve];
setB = [[self retrieve] retain];
[setA release];
[setB release];
}
start EDIT
Based on comments below, the updated retrieve method:
- (NSSet *) retrieve
{
NSMutableSet *set;
set = [[[NSMutableSet alloc] initWithCapacity:100] autorelease];
// would normally fill the set in here with some data
return (set);
}
end EDIT
The above code gives a warning for [setA release] "Incorrect decrement of the reference count of an object is not owned at this point by the caller".
I though that the "new" set the reference count to 1. Then the "retain" call would add 1, and the "release" call would drop it by 1. Given that wouldn't setA have a reference count of 0 at the end and setB have a reference count of 1 at the end?
From what I have figured out by trial and error, setB is correct, and there is no memory leak, but I'd like to understand why that is the case (what is wrong with my understanding of "new", "autorelease", "retain", and "release").
I though that the "new" set the reference count to 1. Then the "retain" call would add 1, and the "release" call would drop it by 1. Given that wouldn't setA have a reference count of 0 at the end and setB have a reference count of 1 at the end?
You're leaving out the autorelease. When -(void)test gets a set, its retain count is 0. You don't retain setA, so it already has a retain count of 0 when you try to release it, hence the error message.
The fundamental rule for memory management is quite simple: calls to alloc, new and copy* must be balanced by calls to release/autorelease. The former take ownership, the latter relinquish ownership.
The only tricky part is when dealing with shared objects, where you don't take ownership of an object, so it might be discarded in between the time you get a reference to it and when you use it. This has a simple solution: if in doubt, retain it.
You can make things even simpler by using properties in many situations.
Don't think in terms of absolute numbers. That can be very deceptive. Think of retains and releases as deltas if you must have a number — in this case, the autorelease has already balanced the new (a +1 delta and a -1 delta), so that method manages its memory correctly and the receiver doesn't need to do anything unless it wants to keep the object around longer.
Definitely read the memory management docs. It really is as simple as following the rules described there. It's a very simple contract of ownership where you claim ownership when you want an object to stick around and relinquish ownership when you don't care anymore. In the case above, you relinquish ownership in the retrieve method, so trying to relinquish ownership when you don't have it is obviously a bug.
As the profiler message hints, you should be thinking in terms of ownership. As noted in the memory management rules, whenever you have an object that you have created with +alloc, +new, -copy, or -mutableCopy, you own it and are responsible for releasing it eventually. (In fact, +new is just shorthand for [[MyClass alloc] init].)
-retain takes an object that you didn't initially own and makes you own it.
-release takes an object that you own and releases ownership of it.
-autorelease takes an object that you own and releases ownership of it, but also guarantees that the object will exist for at least a little bit longer.
Your -retrieve method does not transfer ownership of the object it returns. This is good—it follows the memory management rules (the method isn't +alloc, +new, -copy, or -mutableCopy). Therefore, using -release on it without using -retain is an error. It would be equally valid to not retain or release the result from -retrieve, as long as the object will have a temporary lifetime—your -autorelease guarantees temporary existence of the object.
http://www.macresearch.org/difference-between-alloc-init-and-new
You probably want
NSMutableSet *set = [[NSMutableSet alloc] initWithCapacity: someNumber];
or
NSMutableSet *set = [NSMutableSet setWithCapacity: someNumber];
Related
I want to know the reference count of an object in my program. Can I?
Sure:
int referenceCount = rand();
The real answer, of course, is the retainCount method; NSUInteger rC = [someObject retainCount];. However, the value returned by it is useless.
never never use the retain count in a conditional
the retain count can never be zero
the retain count never reflects whether or not the object is autoreleased
a retain count of 2-bazillion is perfectly reasonable for some classes some of the time
the retain count for any object that passes through system API may be a seemingly random value within indicating a problem
Bottom line: If you treat the retain count as an absolute value, you are doing it wrong. You either increase or decrease the retain count through your code, Keep your plusses and minuses in balance, and you are doing it right.
You can, but you are wrong: you don't want to do that.
Isn't it meant to be...
[obj retainCount];
Never use retainCount, it does not work the way you think it does.
See: SO on finding retains/releases
As everybody said, you can, BUT DON'T USE IT.
Back when I started I also thought that using the ref count I could find my memory problems easier. I wasted TOO MUCH time. The number you get is simply wrong in the sense that you can not (easily) just use it to find your memory management issues.
It is by far better to really check all the manually created objects with alloc, new, and your manual retains.
Simply sit back and focus onthe question "Who has ownership of this variable?" All thouse will keep your var alive. Also be sure to set your ivars with self.ivar, otherwise the ownership to the object will not be set.
But the easiest is to just use ARC in the newest version. This takes care of (most) of all these questions....
Not sure why all the posts about not using retainCount, its been valuable in tracking down memory issues for me, now I wouldn't use it as a conditional, nor even understand how that would even be used, but you can add a simple category and use retain count adequately to determine lifetime usage of any given class.
#implementation replaceWithYourClassName (MemoryInspecting)
- (id) retain
{
NSLog(#"RETAIN: self [%#] : retain count [%d]", [self description], [self retainCount]);
return [super retain];
}
- (void) release
{
NSLog(#"RELEASE: self [%#] : retain count [%d]", [self description], [self retainCount]);
[super release];
}
And throw some breakpoints in there to track the context if that helps, as it often does.
When running Analyzer, I receive the "Object sent -autorelease too many times" warning. I know I'm doing something tricky (and am open to alternatives to accomplishing my goal).
Essentially, I wanted to keep a stack of specific types of controllers handy for sending messages from a central location. So, considering:
From 'ActionBroker' shared object:
NSMutableSet *liveActions;
liveActions = [NSMutableSet alloc] init];
...
CLViewController *action = [[[actionClass alloc] init] autorelease];
if (!action) {
return nil;
}
[self.liveActions addObject: action];
// When adding an object, the retain count is increased. We want the action
// to dealloc, so that it might remove itself from the list...
[action release];
return action;
And the complementary dealloc code:
[[CLActionBroker sharedActionBroker] removeAction: self];
[super dealloc];
... and in removeAction:
[action retain];
[self.liveActions removeObject:action];
The above code works, I just get the nagging error. And the nagging sensation that I could probably solve the problem a different way.
One of the use cases for this code is to pass an 'handleOpenURL' request through a chain of open controllers and returning the first 'YES' response.
As I understand it, your intent is to have a set of controllers ("actions") in the set, which you could easily access from anywhere in your app. If one of these controllers is deallocated, it would automatically remove itself from the set.
There's a problem with this setup, though. As you've described it above, the controller is supposed to be removed from the liveActions set upon deallocation. But since an NSSet retains its members, your controller will never be deallocated so long as it is still in the liveActions set. -dealloc is only run when all retains have been balanced by a release.
This, then leads to your over-release, which leads to the warning. Since you've sent an extra release, -dealloc could be run while the controller is still in the liveActions set. But when you remove it from that set, it's going to send a release message to your object, which would take its retain count negative. That may or may not be safe, but in either case it's an ugly workaround.
What you really want, it seems, is a set that does not retain its members. This is normally a dangerous configuration, since it can lead to dangling pointers in the set. But if you're willing to manage the lifetime of the object and clear out those dangling pointers at the appropriate times, it turns out that it's quite doable. You just need to use a CFMutableSet instead of an NSMutableSet. And since the two are toll-free bridged, it doesn't even add that much complexity.
Setting up the CFMutableSet looks like this:
// The NULL for the third parameter tells the set to not do anything when
// an object is added to or removed from the set.
CFMutableSetRef cfLiveActions = CFSetCreateMutable(NULL, 0, NULL);
// Toll-free bridging makes this possible
NSMutableSet *liveActions = (NSMutableSet *)cfLiveActions;
After that, you can use it exactly as you would use any other NSMutableSet; this one will just be special in that it won't retain its members.
The problem:
CLViewController *action = [[[actionClass alloc] init] autorelease]; // +0 ownership
// ...
[self.liveActions addObject: action]; // liveActions takes ownership
// ...
[action release]; // -1 ownership
When you've alloced the object, you are responsible for it. But then when you autorelease it, you've fulfilled your obligation (and because you autoreleased instead of releasing, you can still use the object until the next turn of the runloop). You shouldn't release it again later.
Also:
[action retain];
[self.liveActions removeObject:action];
The retain is unnecessary.
(And here's another plug for beginning to switch to ARC, under which you won't have to worry about this!)
[Edit: misunderstood your question. Revised answer forthcoming.]
[Edit 2: never mind... even though I misread your intent, I believe my solution is still right.]
What if I have an object, say NSIndexPath in which before I do a copy I always release it first?
Is it possible to have a memory count under 0?
I am doing this to prevent memory leaks.. is this a good way?
//global already has some value before in here or it doesn't have a value.. I want to update
//this with a new value (I no longer care to the old pointer)
[global release]
global = [indexPath copy];
Don't. When the retain count reaches 0, your object will be dealloc'ed and its pointer will become invalid, so using it again will cause unpredictable results (namely crashing).
You should read through Apple's Memory Management Guide.
This is the fundamental rule:
You only release or autorelease objects you own. You take ownership of an object if you create it using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.
You use release or autorelease to relinquish ownership of an object. autorelease just means “send a release message in the future” (specifically: when the used autorelease pool receives a drain message).
Update:
As Josh pointed out, the one case you need to consider is when global and indexPath are the same. In that case, you would still "need" the pointer (to perform the copy), so you either autorelease (instead of releasing) or you use a temporary variable to handle that.
What you are doing is essentially correct, so long as global either has an old value that you no longer need or is nil. If it is one of a class's ivars, it will be nil when an instance of the class is created. The one catch is that if the new indexPath happens to be the same object as the one that's already in global, you will have over-released and you will crash.
// global points to object at 0x8BADFOOD
// indexPath also happens to be 0x8BADFOOD; for some reason, it
// hasn't changed since last time. This _can_ happen.
[global release]; // object at 0x8BADFOOD is deallocated
global = [indexPath copy]; // indexPath no longer valid!
// Crash! Bang! Boom!
The way to avoid this is to use a temp variable.
When you declare a property as copy and synthesize that property, the setter method that is created looks essentially like this, and you can do the same thing:
- (void)setMyFloozit:(Floozit *)newFloozit {
// Copy first in case newFloozit and myFloozit are for
// some reason the same object
// Take ownership of a copy of newFloozit
Floozit * tmp = [newFloozit copy];
// We no longer need old value of myFloozit, so we release it.
[myFloozit release];
// tmp contains a value that we own, we just need to assign
// it to the correct name.
myFloozit = tmp;
}
This could be made slightly better by checking first to see whether newFloozit and myFloozit are the same, and doing nothing if they are.
I have a singleton class that has an mutableDictionary. I initialise the dictionary in my root viewController. later on I would like to empty the dictionary and release the memory. Even though the retain count is 1 the release causes a crash:
-[CFDictionary release]: message sent to deallocated instance
is it possible to release a singleton property?
Thanks
First I'll reiterate what has been said a ton of times on here: Don't call -retainCount!! It is an implementation detail.
See: StackOverflow | when to use retainCount for an excellent recount of why you don't use retainCount.
Beyond that, I'd recommend looking into more information about some of the invariants to shoot for in writing singletons. Dave DeLong has a great recap of how (and more importantly why he does singletons) a certain way. The article includes links to other developers and their outlooks. I'd recommend familiarizing yourself with those tenets and then re-evaluating you implementation.
Finally, just to say it one more time:
Please everyone go to http://bugreport.apple.com and request that -retainCount be deprecated. The more people that ask for it, the better.
You should not be releasing other objects' properties. Allow the singleton to manage the dictionary itself and your design will be simpler.
As for the problem where Cocoa says you're overreleasing even though "the retain count is 1", there is only one good piece of advice you'll ever get about retain counts:
Don't look at them!
They are deceptive and an object's actual memory management is affected by all sorts of forces that the retain count cannot tell you about.
In this case, you're checking the retain count to see if the object still exists. But there is no such thing as an object with a retain count of 0 — when you release an object with a retain count of 1, it's deallocated. Any result you get back from a deallocated object is garbage, so you'll never be able to ask an object for its retain count and get back 0 — objects with a retain count of 0 literally do not exist.
Any object that is allocated can be released later. Sounds like you're over-releasing. Check your properties and all references to the object to make sure you're not over-releasing it. If the dictionary is inside the singleton class, the singleton class should be in charge of releasing it, not the customer viewcontroller.
Agree with the comments re retainCount. Just don't do it.
Initialize the dictionary in the singleton
Add/remove objects from the dictionary as needed in other classes
The overhead of an empty dictionary is trivial, I wouldn't worry about releasing it when empty
Use [dictionary removeAllObjects] to remove everything when needed
If the dictionary is storing objects you want to release in the event of a memory warning, have the singleton observe UIApplicationDidReceiveMemoryWarningNotification and have it remove all it's objects there.
If you really want your implementation to release the entire dictionary I would override the synthesized getter and add singleton methods to interact with the dictionary as follows:
in MySingleton.m:
- (NSMutableDictionary *)myDictionary
{
if (!_myDictionary) {
_myDictionary = [[NSMutableDictionary alloc] init];
}
return _myDictionary;
}
- (void)setObject:(id)object inMyDictionaryForKey:(NSString *)key
{
[self.myDictionary setObject:object forKey:key];
}
- (void)removeObjectInMyDictionaryForKey:(NSString *)key
{
[self.myDictionary removeObjectForKey:key];
if ([self.myDictionary count] == 0) {
self.myDictionary = nil;
}
}
- (void)removeAllObjectsFromMyDictionary
{
self.myDictionary = nil;
}
I have some code which I think has extra release statements.
Is the code incorrect?
What is the end result?
I don't understand memory management well yet - even after reading lots of articles and stackoverflow answers. Thanks for straightening me out.
Update: The attached snippet works fine, but other code has the over-release problem
NSMutableArray *points = [NSMutableArray new];
for (Segment *s in currentWorkout.segments) {
[points addObjectsFromArray:[s.track locationPoints]];
}
[routeMap update:points];
[points release];
Your code is correct, but inadvisable. new acts as an implied alloc, which creates the object with a retain count of 1.
I think the last time I used new was in 1992; it's not wrong, but alloc/init is considered better practice, because it is clearer what you are doing. Please read Apple's guide to memory management, it is a comprehensive summary of the situation.
No messages can safely be sent to a deallocated object. Once an object has been released a sufficient number of times, it's deallocated. Any further messages sent to that object are going to an object that isn't there anymore. The precise result isn't completely predictable, but it usually ends in a crash. If you're less lucky, it could end in much stranger ways — for example, you could theoretically wind up with an Object A getting dealloced early and Object B allocated in the same memory location, then Object B receiving messages meant for Object A that Object B does understand but isn't supposed to receive at that time.
Basically, follow the rules. Think of it in terms of ownership. If you've claimed ownership, you need to release that ownership. If you don't own the object, you must not release it.
Take a look at this article online: http://weblog.bignerdranch.com/?p=2 .
It seems to imply that calls to release without a corresponding preior call to retain will result in a BAD_ACCESS error.
A short answer is, if you increasing the retain count of an object and you no longer are using it you should release it, otherwise you shouldnt...
So when ever you do a [objectName alloc] you are increasing the count by 1, when you use such methods as [NSString stringWithString:] these methods return an autoreleased object so you dont need to release it...if you instead did something like [[NSString stringWithString:]retain] then you are increasing the strings retain count and you should release it after you are done using it.
Im not too sure if new increases the reference count (i suspect that it would), you can always check your retain count by doing [object retainCount]... though note that even if the retain count is greater than 0, it does not mean you need to release the object, because some other class might have a reference to the object and therefore has its retain count increased by one and its the responsibility of the other class holding the reference to release it.
Hope this helps
you should use:
NSMutableArray *points = [[NSMutableArray alloc] init];
[...]
[routeMap update:points]; //if routemap stores the points, it will need it's own release retain
[points release]; //if there is a retain in the method above, reference will not be cleared
if unsure, use the build->analyze command, it will search your code for leaked references
you can get the official memory management guide from https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html