9

I found a strange behavior with NSString. I tried to run the below code and noticed this.

NSString *str = [[NSString alloc] initwithstring : @"hello"];
[str release];
NSLog(@" Print the value : %@", str);

Here, in the third line app should crash because we are accessing an object which is released. But it is printing the value of str. It is not crashing. But with NSArray i observed different behavior.

NSArray *array = [[NSArray alloc] initwithobjects : @"1",  @"2", nil];
[array release];
NSLog(@"Print : %@", [array objectatindex : 0]);
NSLog(@"Print : %@", [array objectatindex : 0]);

The code has two NSLog statements used for NSArray. Here after releasing when the first NSLog is executed, it is printing value. But when second NSLog is executed, app crashes. App crash is acceptable because the array accessed was released already. But it should crash when the first NSLog is executed. Not the second one.

Help me with this behaviors. How release works in these cases.

Thanks Jithen

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Coder
  • 1,661
  • 4
  • 27
  • 50
  • This is not an exact duplicate, but I thought it was worth pointing out this question as it has some partially relevant answers: http://stackoverflow.com/questions/8386084/is-allocinitwithstring-same-as-copy – Steven Fisher Mar 01 '13 at 05:09
  • User @BoltClock's answer [here](http://stackoverflow.com/questions/6069459/does-some-text-give-an-autoreleased-or-retain-1-object-back) also a good read.. – Krishnabhadra Mar 01 '13 at 05:11

5 Answers5

7

The first example doesn't crash because string literals are never released. The code is really:

NSString *str = @"hello";
[str release];

People get burned with string literals on memory management and mistakenly using == to compare them instead of isEqualToString:. The compiler does some optimizations that lead to misleading results.

Update:

The following code proves my point:

    NSString *literal = @"foo";
    NSString *second = [NSString stringWithString:literal];
    NSString *third = [NSString stringWithString:@"foo"]; // <-- this gives a compiler warning for being redundant
    NSLog(@"literal = %p", literal);
    NSLog(@"second = %p", second);
    NSLog(@"third = %p", third);

This code gives the following output:

2013-02-28 22:03:35.663 SelCast[85617:11303] literal = 0x359c
2013-02-28 22:03:35.666 SelCast[85617:11303] second = 0x359c
2013-02-28 22:03:35.668 SelCast[85617:11303] third = 0x359c

Notice that all three variable point to the same memory.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • This is interesting.. Do you have a reference to back this up? – Krishnabhadra Mar 01 '13 at 04:56
  • String literals i agree. But i am allocating memory to NSString object. So it should be freed when a release message is passed. If NSString behaves in that way, then why cant NSArray? – Coder Mar 01 '13 at 04:58
  • No @jithen, here actually you use init with a string literal, since you use `@"hello"`. Any string using `@` notation is created at compile time and has its own memory space, and it is constant. your str is still pointing to the string literal address even after release you could escape the crash since the literal is not released.. But never depend on this kind of behaviour.. – Krishnabhadra Mar 01 '13 at 05:02
  • Apple's not stupid. There's no reason for `[NSString initWithString:]` to copy if the input string is immutable. Therefore, it probably won't. The compiler's understanding of arrays is a lot more basic, however. There's no initial `NSArray` in the code… it's a list of arguments. Not the same thing from a memory perspective. You'd *probably* see the same thing out of `[array copy]`, however, if `array` was immutable. All of this is, of course, implementation detail. You should assume `copy` copies, `init` initializes, etc. – Steven Fisher Mar 01 '13 at 05:03
  • Calling `NSString *foo = [NSString stringWithString:@"Some Literal"]` is identical to `NSString *foo = @"SomeLiteral"`. So you are not really creating an object. Again, this is an implementation detail and optimization. In fact, calling `stringWithString:` with a literal gives a compiler warning. – rmaddy Mar 01 '13 at 05:04
  • Thanks for the explanation. So does NSArray works similar to NSString here? As mentioned in my querry, in NSArray, first NSLog execution is a success and second NSLog does make app crash. It should be with first NSLog i guess. – Coder Mar 01 '13 at 05:06
  • No. `NSArray`s can't (currently) be generated at compile time like `NSString`. Even the `@[]` operator actually operates at runtime. – Steven Fisher Mar 01 '13 at 05:07
  • @jithen No, the array works as expected. There is no optimization being done. It's a regular object that is release as soon as you call `release`. – rmaddy Mar 01 '13 at 05:07
  • 1
    @CarlVeazey has the explanation for the NSArray portion, I agree with it, and this is the absolute correct answer for the NSString portion. – borrrden Mar 01 '13 at 05:09
6

Your second example crashes at the second NSLog because at the first log, the memory where array was hasn't been re-used, but that first log causes enough activity on the heap to cause the memory to become used by something else. Then, when you try to access it again, you get a crash.

Whenever an object is deallocated and its memory marked as free, there is going to be some period of time where that memory still stores what's left of that object. During this time you can still call methods on such objects and so forth, without crashing. This time is extremely short, and if you're running a lot of threads it may not even be enough to get your method call in. So clearly, don't rely on this implementation detail for any behavior.

As others have said, regarding your first question, NSString literals aren't going to be deallocated. This is true for some other Foundation classes (NSNumber comes to mind) but is an implementation detail as well. If you need to do experiments on memory management, use an NSObject instance instead, as it will not show the unusual behaviors.

Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
4

When you send a release message on an object, the object is actually not being removed from the memory. The release message simply decrements the reference count by one only. If the reference count is zero the object is marked as free. Then the system remove it from the memory. Until this deallocation happens you can access your object. Even if you release the object your object pointer still points to the object unless you are assigning nil to the pointer.

Anil Varghese
  • 42,757
  • 9
  • 93
  • 110
  • This doesn't explain the different results between the two blocks of code. And in both cases, the reference count appears to be 0 immediately after the call to `release`. So based on your answer, both should result in a crash but only the second does. – rmaddy Mar 01 '13 at 04:53
  • I was not comparing both the codes. I said in the second part of code i.e, regarding NSArray, i have used two NSLog statements one by one. When i tried accessing NSArray, for the first statements it executes. But for the second time app crashes. – Coder Mar 01 '13 at 04:57
0

The first example doesn't crash because string literals are never released. Where the second totally depends on release and retain counter.

Read this article. Its contains short-and-sweet explanation for your query

You Should read this apple guideline

NSGod
  • 22,699
  • 3
  • 58
  • 66
Nitin
  • 7,455
  • 2
  • 32
  • 51
  • 1
    I agree that it is good for OP to read the apple guideline. But to answer the question, either you quote the relevent part from the apple's guidelines in the answer itself, or link to the specific section in the guidelines. Posting a multipage document as answer is not constructive. – Krishnabhadra Mar 01 '13 at 05:04
0

You seem to assume that release should destroy the object immediately. I don't think that's the guarantee that the language makes. What release means is: I have finished using this object and I promise not to use it again. From that point onwards it's up to the system to decide when to actually deallocate the memory.

Any behaviour you see beyond that is not defined and may change from one version of the Objective C runtime to the next.

That's to say that the other answers that suggest the difference is string literals and re-use of memory are currently correct but assuming that the behaviour will always be like this would likely be a mistake.

Stephen Darlington
  • 51,577
  • 12
  • 107
  • 152