I've written the following code:
NSString *string = [[NSString alloc] initWithFormat:#"test"];
[string release];
NSLog(#"string lenght = %d", [string length]);
//Why I don't get EXC_BAD_ACCESS at this point?
I should, it should be released. The retainCount should be 0 after last release, so why is it not?
P.S.
I am using latest XCode.
Update:
NSString *string = [[NSString alloc] initWithFormat:#"test"];
NSLog(#"retainCount before = %d", [string retainCount]);// => 1
[string release];
NSLog(#"retainCount after = %d", [string retainCount]);// => 1 Why!?
In this case, the frameworks are likely returning the literal #"test" from NSString *string = [[NSString alloc] initWithFormat:#"test"];. That is, it determines the literal may be reused, and reuses it in this context. After all, the input matches the output.
However, you should not rely on these internal optimizations in your programs -- just stick with the reference counting rules and well-defined behavior.
Update
David's comment caused me to look into this. On the system I tested, NSString *string = [[NSString alloc] initWithFormat:#"test"]; returns a new object. Your program messages an object which should have been released, and is not eligible for the immortal string status.
Your program still falls into undefined territory, and happens to appear to give the correct results in some cases only as an artifact of implementation details -- or just purely coincidence. As David pointed out, adding 'stuff' between the release and the log can cause string to really be destroyed and potentially reused. If you really want to know why this all works, you could read the objc runtime sources or crawl through the runtime's assembly as it executes. Some of it may have an explanation (runtime implementation details), and some of it is purely coincidence.
Doing things to a released object is an undefined behavior. Meaning - sometimes you get away with it, sometimes it crashes, sometimes it crashes a minute later in a completely different spot, sometimes a variable ten files away gets mysteriously modified.
To catch those issues, use the NSZombie technique. Look it up. That, and some coding discipline.
This time, you got away because the freed up memory hasn't been overwritten by anything yet. The memory that string points at still contains the bytes of a string object with the right length. Some time later, something else will be there, or the memory address won't be valid anymore. And there's no telling when this happens.
Sending messages to nil objects is, however, legitimate. That's a defined behavior in Objective C, in fact - nothing happens, 0 or nil is returned.
Update:
Ok. I'm tired and didn't read your question carefully enough.
The reason you are not crashing is pure luck. At first I though that you were using initWithString: in which case all the answers (including my original one (below)) about string literals would be valid.
What I mean by "pure luck"
The reason this works is just that the object is released but your pointer still points to where it used to be and the memory is not overwritten before you read it again. So when you access the variable you read from the untouched memory which means that you get a valid object back. Doing the above is VERY dangerous and will eventually cause a crash in the future!
If you start creating more object in between the release and the log then there is a chance that one of them will use the same memory as your string had and then you would crash when trying to read the old memory.
It is even so fragile that calling log twice in a row will cause a crash.
Original answer:
String literals never get released!
Take a look at my answer for this question for a description of why this is.
This answer also has a good explanation.
One possible explanation: You're superfluously dynamically allocating a string instead of just using the constant. Probably Cocoa already knows that's just a waste of memory (if you're not creating a mutable string), so it maybe releases the allocated object and returns the constant string instead. And on a constant string, release and retain have no effect.
To prove this, it's worth comparing the returned pointer to the constant string itself:
int main()
{
NSString *s = #"Hello World!";
NSString *t = [[NSString alloc] initWithFormat:s];
if (s == t)
NSLog(#"Strings are the same");
else
NSLog(#"Not the same; another instance was allocated");
return 0;
}
Related
First code and output:
NSString *text = #"Sunny";
__weak NSString *string0 = text.lowercaseString;
__weak NSString *string1;
string1 = text.lowercaseString;
NSLog(#"%#, %#", string0, string1);
Output:
(null), sunny
But after I move the declaration of string1 above the text, output is different. Here is the code:
__weak NSString *string1;
NSString *text = #"Sunny";
__weak NSString *string0 = text.lowercaseString;
string1 = text.lowercaseString;
NSLog(#"%#, %#", string0, string1);
Output:
sunny, sunny
I am very confused with the different output:
Why string0 and string1 is different in the first case?
Why the output of second case is different with the first?
Trying to figure out exactly when an object is released or a weak reference nulled can be challenging, and often doesn't really help understanding. Here are some reasons why you can see different results than you expect:
NSString: It is best never to use this type when doing these kind of investigations. String literals are immortal, they are not collected, and you may have a string literal even when you don't expect one.
The auto-release pool: The auto-release pool is really a hangover from the pre-ARC days, but it still exists and many methods return auto-released objects. What this means is many objects will live longer than you might expect, but not too long. However ARC has tricks and can remove objects from the auto-release pool early, so you might first think the object will live longer and then it doesn't...
weak references: After the first two bullets you should guess that as you might have no real idea when an object gets released, if at all, then you might have no real idea when a weak reference gets nulled. Just think "soon enough".
Optimisation: There is some leeway in optimisations the compiler can do which, while retaining the correct semantics of your program, may alter the lifetime of objects.
If you do want to run these kind of investigations then you will probably get further if (a) use your own class types, not anything from the libraries and (b) use #autoreleasepool { ... } blocks to limit the lifetimes of auto-released objects.
As an example, when I ran your code on the compiler I was using I did not get a (null), however changing the first assignment to string0 = text.lowercaseString.mutableCopy did produce one... Figuring out why is left as an exercise...
Have an inquiring mind and explore, that is good, but be prepared for the non-obvious!
HTH
In my iOS app I have following code:
case SASpeechSubCase03:
{
SAActivity currentActivity = self.mediator.selectedActivity;
NSString *sActivity = NSLocalizedString(#"activity", #"activity");
NSString *sActivity2 = NSLocalizedString(#"another activity", #"another activity");
if(currentActivity == SAActivityWalk)
{
sActivity = NSLocalizedString(#"walk", #"walk");
sActivity2 = NSLocalizedString(#"walking", #"walking");
}
else
{
sActivity = NSLocalizedString(#"run", #"run");
sActivity2 = NSLocalizedString(#"jogging", #"jogging");
}
return [NSString stringWithFormat:speech.text, sActivity, sActivity2];
break;
}
When I run bots on it, it gave me following warning:
Bot Issue: analyzerWarning. Dead store.
Issue: Value stored to 'sActivity' during its initialization is never read.
File: SAAnnouncementService.m.
Integration Number: 42.
Description: Value stored to 'sActivity' during its initialization is never read.
Bot Issue: analyzerWarning. Dead store.
Issue: Value stored to 'sActivity2' during its initialization is never read.
File: SAAnnouncementService.m.
Integration Number: 42.
Description: Value stored to 'sActivity2' during its initialization is never read.
Can someone tell what the problem might be here?
Any kind of help is highly appreciated!
The problem is that you initialized the variables and then directly started the if-else blocks, without using, i.e. reading, the initial values.
When execution gets to the if-else blocks, it will definitely be assigned a new value, no matter what value it was before.
With the following line :
NSString *sActivity = NSLocalizedString(#"activity", #"activity");
NSString *sActivity2 = NSLocalizedString(#"another activity", #"another activity");
You are assigning string values to the sActivity and sActivity2 objects.
Then, these two values are modified in either if or else statement.
But, as the static analyzer mentions, the initial values of these objects (#"activity" and #"another activity") were never read before the second assignment (in if / else statement).
To avoid this warning you can replace the two lines above, by :
NSString *sActivity = nil;
NSString *sActivity2 = nil;
Hope that helps ;)
When you get a warning, the compiler tells you "what you are doing here looks like nonsense, and is most likely not what you want".
Look at these two statements:
NSString *sActivity = NSLocalizedString(#"activity", #"activity");
NSString *sActivity2 = NSLocalizedString(#"another activity", #"another activity");
Does the assignment serve any purpose? It doesn't look like it. So the compiler thinks "either the guy made a rather expensive call that is completely pointless, or he actually intended to use the result of NSLocalizedString but stored it in the wrong place. "
Since the compiler assumes that people don't do pointless things, it assumes that there is a bug in your code and tells you about it. It's the kind of thing where a human reviewing your code would stop and ask you what you were intending to do there.
In your codes, sActivity would be set to either walk or run within IF/ELSE, so that the value set for sActivity this line
NSString *sActivity = NSLocalizedString(#"activity", #"activity");
would never be read. It might not cause error but analyzer reminded you about this superfluous initialization. Try NSString *sActivity=nil;, see if the warning could be turned down.
You are not using sActivity in if-else blocks, you are simply assigning it values based on decision, So either take it nil string like
sActivity = nil;
or like
NSString *sActivity;
to remove waring .
Was testing some code and found an error with the following lines:
NSString *stringA = #"C99";
NSString *stringB = (__bridge id)malloc(sizeof (stringA));
It is not necessary to alloc a NSString this way, of course, and I am not required to do that. Again I was just testing on something else and I happened to stumble upon this.
The error reads:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)
In the console:
(lldb)
To generalize, perhaps I should ask:
Could we alloc Objective-C objects through the use of malloc?
Has someone encountered this before (which I doubt, because I don't think anyone who uses Objective-C would alloc a NSString this way), but rather than shoving it aside and call it a day, I thought I would ask and see if someone knows what the exact cause of this is and why.
It is possible to use custom allocators for Objective-C objects. The problems with your code include:
NSString is a class cluster superclass (similar to an "abstract class") and cannot be instantiated on its own. You would need to use some concrete subclass of NSString. Note that the OS API does not provide any such class.
sizeof(stringA) is the size of the pointer variable, 4 or 8 bytes, which is too small to hold an NSString instance. You would need to use class_getInstanceSize() to compute the size.
+alloc performs work other than the allocation itself which is not present here. You would need to erase the memory and call objc_constructInstance().
ARC forbids the use of the low-level runtime functions that are needed to accomplish the above tasks.
well as far as I found the closest example of allocating NSSTring Clike is like this:
NSString* s4 = (NSString*)
CFStringCreateWithFormat(kCFAllocatorDefault, 0,
(CFStringRef) __builtin___CFStringMakeConstantString("%# %# (%#)"), s1, s2, s3);
ofcourse if you want to go lower and lower levels of this allocations , you should watch the CFStringRef class for its lower allocation .
but I hope this answer will satisfy you
found here, also there is more interesting things
http://www.opensource.apple.com/source/clang/clang-318.0.45/src/tools/clang/test/Analysis/NSString.m
I think the question you should be asking is what purpose that code serves.
Note that sizeof doesn't return the number of bytes in stringA, it simply returns the size of the pointer that is stringA. Who knows what lives in that little block of memory that has been allocated to stringB. Maybe it's a string, maybe not. Life is full of mystery.
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
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.
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.
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.
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
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.
So I have read this question, which seems to be exactly the kind of problem I am having, but the answer in that post does not solve my problem. I am attempting to write a data serialization subclass of NSMutableData. The problematic function header looks like this:
-(void)readString:(__autoreleasing NSString **)str
I do some data manipulation in the function to get the particular bytes the correspond to the next string in the data stream, and then I call this line:
*str = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
No errors in this code. But when I try to call the function like so:
+(id) deserialize:(SerializableData *)data
{
Program *newProgram = [[Program alloc] init];
[data readString:&(newProgram->programName)];
On the line where I actually call the function, I get the following error:
Passing address of non-local object to __autoreleasing parameter for write-back
I have tried placing the __autoreleasing in front of the NSString declaration, in front of the first *, and between the two *'s, but all configurations generate the error.
Did I just miss something when reading the other question, or has something in the ARC compiler changed since the time of that post?
EDIT:
It seems that the problem is coming from the way I am trying to access the string. I can work around it by doing something like this:
NSString* temp;
[data readString&(temp)];
newProgram.programName = temp;
but I would rather have direct access to the ivar
You can't. You might gain insight from LLVM's document Automatic Reference Counting, specifically section 4.3.4. "Passing to an out parameter by writeback". However, there really isn't that much extra detail other than you can't do that (specifically, this isn't listed in the "legal forms"), which you've already figured out. Though maybe you'll find the rationale interesting.