Releasing an autoreleased string object doesnt crash - objective-c

I have this basic question where when we try to allocate memory to a string by using alloc init and add it to autorelease pool, and then try to release it, it doesn't crash.
NSString *value = [[[NSString alloc] initWithString:#"Hello"] autorelease];
[value release];
If I do the same thing for a array, it crashes.
I just want to know how string is different from array since both inherit from NSObject.

Do you mean it doesn't crash right away?
I.e. in the debugger after stepping over the release line?
The Autorelease-Pool will not have triggered at that point so the auto-release operation is still outstanding.
Either way - As always with memory errors they might not crash
instantly,
on your machine/operating system,
with this specific built,
with your current build settings,
...
or even at all.
It's a programming error nevertheless.

It is not guaranteed that the program crashes. Unfortunally there are several errors that does not cause a crash (immediately).
However there is an optimization for string literals. They live eternally regardless of the way they are created, retained or released. -initWithString: can be smart enough not to return a new instance of NSString.

First, undefined behavior is undefined -- it is not guaranteed to crash or do any other specific thing. Over-release is undefined behavior.
Second, what is happening here is that string literals evaluate to a pointer to a statically-allocated string object which lives for the lifetime of the program. It is not dynamically-allocated, and thus is not subject to memory management. retain, release, etc. have no effect on it. [[NSString alloc] initWithString:...] (as well as [... copy]) on an immutable string object simply retains and returns its argument directly, since there is no need to create a new object. So [[NSString alloc] initWithString:#"Hello"] is the same as #"Hello".

Related

Does this cause leak of NSString?

I am a C programmer, but is pretty new to Objective-C. I've seen NSString-related code like this several times:
NSAppleScript* script = [[NSAppleScript alloc] initWithSource: #"<some script code>"];
...
[script release];
The above code explicitly releases the NSAppleScript object, but doesn't seem to release the NSString object anywhere.
I wondered if the [script release] automatically does the job of implicit release of the NSString object, so I changed the above code to the following:
NSString* scriptText = #"<some script code>";
NSAppleScript* script = [[NSAppleScript alloc] initWithSource: scriptText];
...
[script release];
//If [script release] has implicitly released scriptText,
//this would cause a repeated release.
[scriptText release];
But the above code turned out running also well. Does it mean that [script release] doesn't automatically release the NSObject object? In other words, does the code in the first section leak release of the NSString?
Very short answer: no. You shouldn't release scriptText. (In fact, you must not.)
In ObjC manual reference counting, you need to follow the rules, which are based on method names. If you call a method whose name begins with alloc or new or includes copy, then you are responsible for calling release or autorelease on the object that is returned to you. Also, if you call retain on an object, you are responsible to call release or autorelease.
Following the rules, you called +[NSAppleScript alloc], so you are responsible for calling release on the object returned to you. You did not call a retaining method to get scriptText; you used an NSString literal (#"..."). So you must not call release on it. It doesn't leak. (If it did, it would indicate a bug in Apple's code.)
The way this actually works is that NSString literals are stored directly in the binary, just like in C. There is no need to manage them, since they do not directly use memory. But this has nothing to do with your obligations under manual reference counting. You shouldn't think "this is a string literal so I shouldn't call release on it." That's not true at all. You should call release when the rules tell you to call release. It is completely correct to call retain on a value that happens to be a literal, and later call release on it. (This happens all the time. You generally don't know whether the NSString you're working with is a literal or not.)
It happens to be true that calling retain or release on NSString literals does nothing. They just ignore the call. Very short NSString objects don't even exist in memory. If they're short enough, the data is stored directly in the pointer (called a "tagged pointer"). Again, this is just an implementation detail. Your job is to follow the rules, not try to second-guess the system.
(The reason that your incorrect code with the extra release "works" is because literal NSStrings ignore memory management calls. The code is still incorrect. There's also no promise that over-releasing an object will cause a crash in any case, and it is very, very common when it does crash for it to occur at a random point, far away from the mistake. It's very common for objects to have pending autorelease calls on them, so you get a crash when the pool drains, with no hint where your bug is.)
And of course you should turn on ARC and let it handle it for you. It does a very good job. But it's helpful to understand the rules anyway. ARC uses the same name-based rules to figure out where to put retains and releases. That's how it can interoperate seamlessly with manual memory management.

Why dose the NSString become NSZomie in this code?

I just ran into a problem.I use NSMutableArray to keep the Ad ids that need to be loaded. When one id is preloaded, I remove it from the array. Then I find out that, after remove, the unit id became a zombie.
I tried to reproduce this, and I find out that when the Ads id was pulled from array, it is not a zombie.It just become zombie after removed from array. But, there is still a NSString* refer to it, how can this happen? And If it will become zombie at this point, it should become zombie every time. But it only happens occasionally.
-(void)preloadNextRewardVideo
{
if([_allRewardVideoAds count])
{
NSString* adsName = [_allRewardVideoAds objectAtIndex:0]; //the element is not a zombie here
GADRewardBasedVideoAd* ads = [self ensureRewardVideo:adsName];
if(![ads isReady])
{
_currentRewardVideoName = adsName;
[_allRewardVideoAds removeObjectAtIndex:0];
GADRequest *request = [GADRequest request];
[ads loadRequest:request withAdUnitID:adsName]; //here, adsName is a zombie
_isRewarLoading = YES;
}
}
}
It's worth taking a close look at cocoa's memory management policies. The thing here (it seems to me) is that when your code assigns the string adsName, the object the preloadNextRewardVideo method is part of doesn't take ownership of the string-object that adsName is pointing at ('ownership' in this context means either allocating and initializing space for it through alloc/init, new, copy, etc, or sending it an explicit retain message). All you have is a local variable that points at a string-object owned by the _allRewardVideoAds array. Yes, making the assignment increases the retain count, but that retain is autoreleased, and limited to the scope of this method. As soon as this method ends, nothing is going to own that string-object, and it will deallocate.
This would not be an issue (and would not create the NSZombie flag) except that you just sent adsName to a different object (GADRewardBasedVideoAd* ads) that I'm guessing does not retain it either. Of course, ads is also autoreleased (nothing owns it outside of this method), but what is that, a race-condition over whether ads or adsName is deallocated first? I suspect you do have a race-condition because NSZombie only shows up sometimes, but I don't know enough about the internal mechanisms of autorelease to know how that might work.
I think NSZombie is just telling you that you have an object that is:
in use by an external object, and
due to be destroyed shortly though autorelease.
ARC is anticipating a problem, and using NSZombie to let you know.
You could fix this either by using the property setter (e.g. use self.currentRewardVideoName = adsName) which retains the string-object globally for this object, or by locally copying it (NSString* adsName = [[_allRewardVideoAds objectAtIndex:0] copy]) which makes sure that your object owns the string to the end of the method.
Since you don't enable ARC that means you need to manually manage the memory by yourself. Since you don't enable ARC that means you need to manually manage the memory by yourself. Even though most of APIs will return an auto released object from a method but that's not always be the case.
For this case, NSArray retains the objects contained in itself so it may not need to return an auto released object from objectAtIndex: method since it should be retained by itself as an optimization. In this case I suggest you to call retain the object when you get it from the array and release it before the end of this method. That would help for this case.

How memory is managed for an object that is created but not assigned to any pointer?

This might be a stupid question, but it keeps bothering me.
Say if we have a method that takes an NSString object as its parameter and does something with the NSString object,
- (void)someMethod:(NSString *)str
{
//do something with str
}
Consider this code
[someObject someMethod:[[NSString alloc] initWithFormat:#"Hello World!"]];
Since alloc has been used in creating the string as parameter of someMethod, it has to be balanced by release no matter explicitly in pre-ARC environment or implicitly under ARC. But it seems there is no way we can get a pointer to the string as we have never assigned it to any pointer.
So my question is, first, is this way of passing parameter prohibited in writing objective c code? If no, then how objects created this way get released? And finally, does this code lead to memory leak?
Just for the record, I understand the above code is written
NSString *string = [[NSString alloc] initWithFormat:#"Hello World!"];
[someObject someMethod:string];
// [string release]; depending on ARC or non-ARC
Well, in fact, that object is assigned to the variable named str, which is a parameter of your method. You can manage the memory inside your method via that pointer, although methods aren't supposed to take ownership of their arguments (except see below).
ARC knows what to do in this situation -- it will either autorelease the object or add a release once the method is finished.
Under MRR, your snippet would be a leak; the correct way to avoid that is also to send autorelease:
[someObject someMethod:[[[NSString alloc] initWithFormat:#"Hello World!"] autorelease]];
or to use your last snippet (putting the string into a temporary variable and releasing later).
As a slightly esoteric option, it is possible for your method to declare that it owns the argument, by using the ns_consumed attribute:
- (void)someMethod:(NSString *) __attribute__((ns_consumed)) str;
This indicates that your method should send release to the object before it returns -- ARC will also take care of that.
So my question is, first, is this way of passing parameter prohibited in writing objective c code?
No. It's perfectly legal.
If no, then how objects created this way get released?
ARC will take care of it for you. If you do your own reference counting, then you can add it to the autorelease pool before it goes out of scope:
[someObject someMethod:
[[[NSString alloc] initWithFormat:#"Hello World!"] autorelease]];
^^^^^^^^^^^
And finally, does this code lead to memory leak?
Not in ARC. In MRC, you would need to add the -autorelease.
The static analyzer would also point out that leak.
There's no reason to not write code as you ask for consideration on… nothing prohibited in the slightest. These objects get released in the same manner that any other object gets released. Your lack of a variable to store the pointer in at the top level isn't important because the Objective C runtime knows about the object.

why does release return (oneway void) but autorelease return (id)?

If I do this-
SomeObject* someObject = [[SomeObject alloc] init];
[[someObject release] release];
I presume my program wouldn't crash because the second over-release is acting on nothing.
Whereas
[[someObject autorelease] autorelease];
would crash because the object is released twice by being passed down the chain.
This is a matter of curiousity only, I guess I've never had a problem with it so far.
(See this answer for an explanation what oneway does.)
Your first example doesn't even compile, as void != nil. But if you simply call release two times on an object that has a retain count of 1, it'll for sure crash.
As for why release returns void while autorelease returns id: The first says I don't need this object any more, you may get rid of it. Thus, if it would return something and you would operate on it you're likely to operate on a dead object. By contrast, autorelease more or less says "Soon I won't be needing the object any more but let it stick around until I'm done with the current method."
So as you see they are similar in that they say I won't be needing this object any more, but they differ in the time: release says "you can get rid of the object now", while autorelease says "you may get rid of the object later".
I suppose this is mainly done for ease of use, as one could of course do without (and put every autorelease on its own line):
Whenever you release an object, you're done with it (or at least you should be). There is no need to return the object as it may well have been destroyed already and in any case the caller should not work with the object if it is not yet destroyed (i.e. someone else has a reference to it).
On the other hand autorelease is often used in conjunction with init/alloc and just marks the object for later destruction. Whenever using autorelease one can almost be sure that the object is being used afterwards. Whether you directly return it or work with it on your own (e.g. after [[[SomeClass alloc] init] autorelease]) it is very handy to have the object returned as it saves an explicit line of code for autorelease. It is also helpful to have the autorelease directly in the same line: On return [someObject autorelease] you can directly see, that this is an autoreleased object. If you use it with init/alloc you can directly see that this object does not need another release.
I believe, you've answered your question yourself. Call to autorelease can be in the very beginning of object's life, so probably you'll need to save some code lines by the following statement:
[anotherObject callMethodWithArg: [myObject autorelease]];
Indeed, that's helpful. However, you call a release, in the very end of the code, that uses your object. By saying immediate release, you are saying you don't need this object here anymore. So there's no sense in reusing it's instance. Another option is, that a release call can actually deallocate the object immediately, so returning an invalid pointer will not be a good idea =)

Object release behavior in iPhone

If I release mainPath in following example the program gives an error (because I’m releasing an object with zero counter)
NSString *mainPath = [NSString stringWithFormat:#"%#/Documents/downloadFile.plist",NSHomeDirectory()];
NSLog(#"address is = %#",mainPath);
[mainPath release]; //Program failed here
But the following code works fine.
NSString *aa=#"hiiiii";
[aa release];
Can anyone explain this?
Actually I’m not clear about the pointer concept (give a suitable link to clear it)
Constant NSStrings are a special case. They are allocated statically at compile time and can't be deallocated. You can send release to a constant string as many times as you like, it'll never get deallocated. This is achieved in the current implementation by setting the retain count to INT_MAX which is treated as a special value meaning "don't decrement me on release".
You should read the Cocoa Memory Management Guide or at least the Objective-C Tutorial by Scott Stevenson. (Really. Do it, you’ll save a lot of time in the long run.) The difference is that the first string is autoreleased, you do not own it and should not release it. The second string is special, I think it’s not allocated on the heap at all and the release is essentially a no-op here.
String with format is a convenience method that autoreleases the string so in this case you are likely to be sending a release message to an already deallocated object.
In your second example, you are creating the string statically so retain counts don't apply.
You don't need to release your objects in either of those cases.
As a rule of thumb, if you didn't use init (or initWithFoo:) to create the object, and didn't deliberately use retain to retain the object (plus couple of other rarer cases), you don't need to use release.