I'm attempting to narrow down a bug to a minimum reproducible case and found something odd.
Consider this code:
static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (staticString == nil) {
staticString = [[NSArray arrayWithObjects:#"1", #"2", #"3", nil] componentsJoinedByString:#","];
}
[pool drain];
NSLog(#"static: %#", staticString);
return 0;
}
I'm expecting this code to crash. Instead, it logs:
2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static:
However, if I change the NSLog() to:
NSLog(#"static: %s", [staticString UTF8String]);
Then it does crash.
edit a bit more info:
After draining the pool:
NSLog(#"static: %#", staticString); //this logs "static: static: "
NSLog(#"static: %#", [staticString description]); //this crashes
So apparently invoking a method on the string is good enough to get it to crash. In that case, why doesn't logging the string directly cause it to crash? Shouldn't NSLog() be invoking the -description method?
Where is the second "static: " coming from? Why isn't this crashing?
Results:
Both Kevin Ballard and Graham Lee are correct. Graham's correct in realizing that NSLog() is not invoking -description (as I was erroneously assuming), and Kevin is almost definitely correct that this is a weird stack-related issue with copying a format string and a va_list around.
NSLogging and NSString does not invoke -description. Graham elegantly showed this, and if you trace through the Core Foundation sources that do the logging, you'll see that this is the case. Any backtrace originating inside NSLog shows that it invokes NSLogv => _CFLogvEx => _CFStringCreateWithFormatAndArgumentsAux => _CFStringAppendFormatAndArgumentsAux. _CFStringAppendFormatAndArgumentsAux() (line 5365) is where all of the magic is going on. You can see that it's manually going through to find all the % substitutions. It only ends up invoking the description copy function if the type of the substitution is a CFFormatObjectType, the description function is non-nil, and the substitution hasn't already been handled by another type. Since we've shown that the description is not getting copied, it's reasonable to assume that an NSString gets handled earlier (in which case it's probably going to be doing a raw byte copy), which leads us to believe...
There's a stack error going on here, as Kevin surmises. Somehow the pointer that was pointing to the autoreleased string is getting substituted to a different object, which happens to be an NSString. So, it doesn't crash. Weird. However, if we change the type of the static variable to something else, like an NSArray, then the -description method does get called, and the program does crash as expected.
How truly and utterly strange. Points go to Kevin for being the most correct about the root cause of the behavior, and kudos to Graham for correcting my fallacious thinking. I wish I could accept two answers...
My best guess for what you're seeing is that NSLog() copies the format string (probably as a mutable copy), and then parses the arguments. Since you've dealloc'd staticString, it just so happens that the copy of the format string is being placed into the same location. This is causing you to see the "static: static: " output that you described. Of course, this behavior is undefined - there's no guarantee it will always use the same memory location for this.
On the other hand, your NSLog(#"static: %s", [staticString UTF8String]) is accessing staticString before the format string copy happens, which means it's accessing garbage memory.
Your assumption that NSLog() calls -description on an NSString instance is faulty. I just added this category:
#implementation NSString (GLDescription)
- (NSString *)description {
NSLog(#"-description called on %#", self);
return self;
}
#end
It doesn't cause a stack overflow, because it doesn't get called recursively. Not only that, but if I insert that category into the code in your question, I find this output:
2011-01-18 23:04:11.653 LogString[3769:a0f] -description called on 1
2011-01-18 23:04:11.656 LogString[3769:a0f] -description called on 2
2011-01-18 23:04:11.657 LogString[3769:a0f] -description called on 3
2011-01-18 23:04:11.658 LogString[3769:a0f] static: static:
so we conclude that NSLog() doesn't call -description on an NSString it comes across in its args. Why you get the static string twice is likely a quirk of the data on the stack when you erroneously access the released staticString variable.
Accessing dealocated memory does not necessarily cause a crash. The behavior is undefined. You are expecting too much!
Maybe it has something to do with the #"static:" being stored in the same memory location as staticString. staticString will be deallocated and it stores the #"static: %#" in that recycled mem location, so then the staticString pointer is on "static: %#" so it ends up static: static:.
This is a case of "Use after free()". What happens is "undefined behavior". Your example is really no different than:
char *stringPtr = NULL;
stringPtr = malloc(1024); // Example code, assumes this returns non-NULL.
strcpy(stringPtr, "Zippers!");
free(stringPtr);
printf("Pants: %s\n", stringPtr);
What happens at the printf line? Who knows. Anything from Pants: Zippers! to Pants: (...garbage...) Core Dump.
All the Objective-C specific stuff is actually irrelevant- it's the fact that you're using a pointer to memory which is no longer valid is the only thing that matters. You're better off throwing darts at the wall than trying to explain "why" it's not crashing and printing static: static. For performance reasons, most malloc implementations don't bother "reaping" free()'d allocations until they have to. IMHO, this is probably why your example isn't crashing in the way you were expecting it to.
If you really want to see this particular program crash, you can do one of the following:
Set the environment variable CFZombieLevel to 17 (scribble + don't free).
Set the environment variable NSZombieEnabled to YES.
Set the environment variable DYLD_INSERT_LIBRARIES to /usr/lib/libgmalloc.dylib (see man libgmalloc).
Related
I’ve written a piece of test code "AlertTest" to make sure I’m implementing NSAlert object the proper way. It consists of a button which triggers [doSomething: cstr] printing a C-string, and a method posting an alert, which passes the same string to the same method, but from within the completion handler.
This is the debugger console printout:
-from [buttonPressed]
2015-01-09 18:28:09.832 AlertTest[1260:40881] Application must quit
Application must quit
-from [mPostError:cstr]
2015-01-09 18:28:12.276 AlertTest[1260:40881] #Èø_ˇ
#\351\277_\377
I’m a bit confused on what I have to do to properly pass a string from within the completion handler. No compiler errors reported. I must be missing something very obvious. Thanks in advance. Here’s the code:
//___________________________________________________________
- (IBAction)buttonPressed:(NSButton *)sender {
char cstr[256];
strcpy(cstr, "Application must quit");
[self mDoSomething:cstr];
[self mPostError:cstr];
}
//___________________________________________________________
-(void)mDoSomething: (char*)cstr
{
NSLog(#"%s",cstr);
printf("%s\n", cstr);
}
//___________________________________________________________
-(void) mPostError: (char *)cstr
{
char cMessage[64] = "Critical Error";
NSString *message = [NSString stringWithCString:cMessage encoding:NSASCIIStringEncoding];
NSString *infoText = [NSString stringWithCString:cstr encoding:NSASCIIStringEncoding];
NSAlert* mAlert = [[NSAlert alloc] init];
[mAlert setMessageText:message];
[mAlert setInformativeText:infoText];
[mAlert setAlertStyle: NSCriticalAlertStyle];
[mAlert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[self mDoSomething: cstr]; //this is the suspect line!
}];
}
Your problem is nothing to do with C-strings but with variable lifetime.
Your execution flow goes as follows:
buttonPressed: is called. This creates a local variable cstr and calls mPostError:
mPostError: is called. This calls beginSheetModalForWindow:completionHandler: passing it a block. That block references cstr so a copy is made of the value in cstr, and that value is the address of buttonPressed:'s local variable of the same name.
mPostError: returns which gets us back to...
buttonPressed: which in turn returns destroying its local variable cstr. The block passed to beginSheetModalForWindow:completionHandler: now has the address pointing into released memory.
The user dismisses the alert and the block is called. That block calls mDoSomething: passing it the, now invalid, address.
mDoSomething: calls printf which tries to interpret the memory at the passed address as a C-string, but by now that memory has been re-used for something else and it produces the nonsense you see.
Passing a C-string is not a problem, but whatever value (a char * value in this case) is passed to a block must be valid at the time the block is executed.
You can "fix" the issue in your example by making cstr a global variable:
char cstr[256]; // global
- (IBAction)buttonPressed:(NSButton *)sender {
strcpy(cstr, "Application must quit");
Now cstr always exists and the address the block copies is therefore always valid.
Of course you've now also permanently used up 256 bytes of memory and if this string is only required for a short period that is a (small) waste.
In your real code this may not be an issue - the C strings may be managed already so they live as long as required. If not and using C strings is a requirement then you may need to look at dynamically allocating them (see malloc and friends) and releasing them when no longer needed (see free).
HTH
I ran into this problem while trying to fix a memory leak with the facebook-ios-sdk. How do i handle this situation when passing objects from no arc compiled classe to arc enabled classe?
This is the code inside the non arc compiled Facebook library: (i removed the unnecessary stuff which is not related to the problem) as you can see, result object is not autoreleased or released.
- (void)handleResponseData:(NSData *)data {
NSError* error = nil;
id result = [self parseJsonResponse:data error:&error];
self.error = error;
// Call the defined delegate wich is my AppDelegate didLoad method wich is arc enabled
[_delegate request:self didLoad:result];
}
- (id)parseJsonResponse:(NSData *)data error:(NSError **)error {
SBJSON *jsonParser = [[SBJSON alloc] init];
//gets the object wich leaks or gets overreleased
id result = [jsonParser objectWithString:responseString];
[jsonParser release];
return result;
}
Now if i try to add autorelease to the result object, i am facing a NSZombie when my arc code in my AppDelegate try's to release the object. However if i leave the code like this i'm facing memory leaks whit the result object which gets not released.
am i missing something basic? i can't get my head around this?
Thanx for any advice! Chris
The result returned from -parseJsonResponse:... is autoreleased already (see note at bottom).
Since the name of the -parseJson method doesn't begin with new, alloc, copy or mutableCopy, the compiler assumes that it returns an object with a +0 reference count, meaning it needs to be retained by the calling code if it is to be kept around, and doesn't need to be released if it's not being kept around. That's a long winded way of saying that it should neither leak nor cause a crash in your ARC code as written in your question.
Passing objects between ARC code and manual reference counting code doesn't require any special handling. You just need to make sure that methods' names match their memory management semantics in the non-ARC code. It certainly seems like you've done that in this case, although as you say, you didn't post your complete code.
Note: Presumably, objectWithString: returns an autoreleased object. If it doesn't it, it should (because it doesn't start with alloc, new, copy, mutableCopy).
I have the following code
- (NSString *)stringByEscapingXMLEntities;
{
NSString *result;
#autoreleasepool {
result = [self stringByReplacingOccurrencesOfString:#"&" withString:#"&"];
result = [result stringByReplacingOccurrencesOfString:#"\"" withString:#"""];
// ... lot of -stringByReplacingOccurrencesOfString: calls
result = [result stringByReplacingOccurrencesOfString:#" " withString:#" "];
}
return result;
}
I ask myself now how would I transfer ownership result out of the method. Before ARC I would have retained result before exiting the autorelease block and returned it autoreleased at the end of the method.
Thanks!
There are two ways to do that:
Rename the method to something like copyStringByEscapingXMLEntities -- the copy indicates the transfer of ownership and ARC creates the code accordingly.
Append, in the header, NS_RETURNS_RETAINED to the method definition like this: - (NSString *)stringByEscapingXMLEntities NS_RETURNS_RETAINED.
EDIT: As 'iljawascoding' mentioned, the #autoreleasepool has no real need to be kept around -- except for optimization.
EDIT 2: And remember: ARC always does the right thing. All the things you tried (your comment) result in the very same correct program -- albeit with the lack of some optimization if result was defined as __strong.
Get rid of the autorelease altogether. According to Apple, ARC will automatically insert the necessary release after your code is done with the temporary instances stored in 'result'. There's nothing to gain by rolling your own autorelease pool here.
The code you have posted is correct. Any crash has a different cause.
Because result is a strong reference outside the autorelease scope, ARC is responsible for keeping it alive when exiting the pool, and it does. You don’t need to do anything special.
More specifically, ARC generates code equivalent to this:
void *_arp = objc_autoreleasePoolPush();
temp1 = objc_retainAutoreleasedReturnValue([self stringByReplacingOccurrencesOfString:#"&" withString:#"&"]);
temp2 = objc_retainAutoreleasedReturnValue([temp1 stringByReplacingOccurrencesOfString:#"\"" withString:#"""]);
objc_release(temp1);
result = objc_retainAutoreleasedReturnValue([temp2 stringByReplacingOccurrencesOfString:#" " withString:#" "]);
objc_release(temp2);
// result is not released here
objc_autoreleasePoolPop(_arp);
return objc_autoreleaseReturnValue(result); // Result is returned autoreleased, or handed off to a matching objc_retainAutoreleasedReturnValue() in the caller.
Note that the temporary variables are handled with objc_retainAutoreleasedReturnValue/objc_release pairs. Because of the runtime optimization implemented by objc_retainAutoreleasedReturnValue and objc_retainAutoreleasedReturnValue, this means that the temporary values will actually be released immediately without ever being put in an autorelease pool if -stringByReplacingOccurrencesOfString: is built with ARC. My guess would be that most of the system frameworks aren’t yet, though.
Why not use [[NSString alloc] initWithString:result] before the #autoreleasepool scope closes? But why the ARP in the first place?
I'm trying to retain a reference to a Block that's been passed in to my class by a method, to call at a later time. I'm having trouble, however, maintaining a reference to it.
The obvious way, I thought, was to add it to an ivar collection, all of which are supposed to maintain strong references to their contents. But when I try to pull it back out, it's nil.
The code is pretty simple:
typedef void (^DataControllerCallback)(id rslt);
#interface DataController : NSObject {
NSMutableArray* queue;
}
- (void) addBlock:(DataControllerCallback)callback;
- (void) functionToBeCalledLater;
#end
#implementation DataController
- (id) init {
self = [super init];
if (self != nil) {
queue = [NSMutableArray new];
}
return self;
}
- (void) addBlock:(DataControllerCallback)callback {
NSDictionary* toAdd = [NSDictionary dictionaryWithObjectsAndKeys:
[callback copy], #"callback",
#"some other data", #"data", nil];
[queue addObject:toAdd];
}
- (void) functionToBeCalledLater {
NSDictionary* dict = [queue lastObject];
NSLog(#"%#", [dict objectForKey:#"data"]; //works
DataControllerCallback callback = [dict objectForKey:#"callback"]; //this is nil
callback(#"an arguemnt"); //EXC_BAD_ACCESS
}
What's happening?
Update: I've tried it with [callback copy] and just callback inserting into the dictionary, neither works.
Update 2: If I just stick my block into an NSMutableSet, as long as I call copy, I'm fine. It works great. But if it's in an NSDictionary, it doesn't.
I've actually tested it by putting a breakpoint right after the NSDict is created and the callback never gets inserted. The description reads clearly "1 key-value pair", not two.
I'm currently getting around this with a specialised class that just acts as a container. The callback property is declared as strong; I don't even need to use copy.
The question still stands, though: why is this happening? Why won't an NSDictionary store a Block? Does it have something to do with the fact that I'm targeting iOS 4.3 and thus ARC must be built in as a static library?
Update 3: Ladies and gentleman: I am an idiot.
The code I presented here was obviously a simplified version of the actual code; most particularly, it was leaving some key/value pairs out of the dictionary.
If you're storing a value in an NSDictionary using [NSDictionary dictionaryWithObjectsAndKeys:], you had better be damn sure one of those values isn't nil.
One of them was.
ICYMI, it was causing an early termination of the argument list. I had a userInfo-type argument being passed into one of the "add to queue" methods, and you could, of course, pass in "nil". Then when I constructed the dictionary, chucking in that argument caused the constructor to think I had terminated the argument list. #"callback" was the last value in the dictionary constructor and it was never being stored.
Contrary to popular mis-conception, ARC does not automatically de-stackify Blocks passed as arguments to methods. It only de-stackify's automatically when a block is returned from a method/function.
I.e. this....
[dict setObject: ^{;} forKey: #"boom"];
... will crash if dict survives beyond the scope and you attempt to use the block (actually, it won't in this case because that is a static block, but that is a compiler detail that you can't rely on).
This is documented here:
How do blocks work in ARC?
Blocks “just work” when you pass blocks up the stack in ARC mode, such
as in a return. You don’t have to call Block Copy any more. You
still need to use [^{} copy] when passing “down” the stack into
arrayWithObjects: and other methods that do a retain.
The return value behavior could be automated because it is always correct to return a heap based block (and always an error to return a stack based block). In the case of a block-as-an-argument, it is impossible to automate the behavior in a way that would be both very efficient and always correct.
The analyzer likely should have warned about this use. If it didn't, file a bug.
(I derped a stack when I meant a heap. Sorry about that.)
The compiler doesn't automate blocks-as-parameters for a few reasons:
unnecessarily copying a block to the heap can be a significant performance penalty
multiple-copies of a block can multiply that performance penalty significantly.
I.e.:
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
If that were to imply four Block_copy() operations and aBlock contained a significant quantity of captured state, that'd be a huge potential hit.
• There are only so many hours in the day and automating the handling of parameters is rife with non-obvious edge cases. If this were handled automatically in the future, it could be done without breaking existing code and, thus, maybe it will be done in the future.
I.e. the compiler could generate:
aBlock = [aBlock copy];
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
doSomethingSynchronous(aBlock);
[aBlock release];
Not only would this fix the problem of a block-as-param, but it would also only produce one copy of the block across all potential uses.
The question still stands, though: why is this happening? Why won't an
NSDictionary store a Block? Does it have something to do with the fact
that I'm targeting iOS 4.3 and thus ARC must be built in as a static
library?
Something bizarre is going on, then. Coincidentally, I've been using blocks-as-values in an ARC based application in the last week and it is working fine.
Do you have a minimal example handy?
I am facing some strange memory leak in our existing iPad application,
Here is a function which gives memory leak in instrument
-(NSString *)retriveInfo:(NSString*)fromstring:(NSString*)searchstring
{
NSArray *arrRetrive = [fromstring componentsSeparatedByString:searchstring];
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1){
NSString *strDisplayOrder = [arrRetrive objectAtIndex:1];
arrRetrive = [strDisplayOrder componentsSeparatedByString:#"<"]; //MEMORY LEAK
}
}
return [arrRetrive objectAtIndex:0];
}
Here is a input parameter
Param 1 : <displayorder>1</displayorder><filename>201103153_0100.pdf</filename><title>【1面】東日本巨大地震直撃、日経平均一時675円安[br]原発関連売られる、東電はS安</title><category>トップ・注目株</category><dirpath>/var/www/mssite/webapp/data/pdf/20110315</dirpath><TimeStamp>201103141700</TimeStamp><FirstPageImg>20110315top.png</FirstPageImg></pagedata>
Param 2: <displayorder>
Basically i want to found (parse) value between start and end tag.
(I know NSXMLParser class but asper i explain this one is existing code and if i changed in code its too much time consuming)
Any suggetion?
With Regards
Pankaj Gadhiya
The code you've posted doesn't look like it's leaking memory -- all the methods you're calling are of the autorelease type (i.e. there's no new, alloc, copy, or retain in the code).
It's probably the code you have that calls retrieveInfo and does something with the result that's leaking memory (e.g. overretaining it). The leaks tool is pointing you at the componentsSeparatedByString because that's where the memory was allocated which was eventually involved in a memory leak.
Can you show us how you call retrieveInfo and what you do with the result?
Btw, what's the point of this nested if?
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1)
It's wasteful, you might as well write this and get the same effect:
if ([arrRetrive count] > 1)
That leak probably means that you're over-retaining the return value. And leaks-tool just shows you the place where it was created.
You are leaking the NSArray when you re-assign a new NSArray inside the if block. The pointer to the original array is lost, which means that the runloop cannot release the memory allocated for the first result. The memory allocated for the second result is released.
You should really use a proper parser... however to address the leak, the following will work.
-(NSString *)retriveInfo:(NSString*)fromstring:(NSString*)searchstring
{
NSArray *arrRetrive = [fromstring componentsSeparatedByString:searchstring];
NSString *result = nil;
if([arrRetrive count]!=0){
if ([arrRetrive count]!=1){
NSString *strDisplayOrder = [arrRetrive objectAtIndex:1];
result = (NSString *)[[strDisplayOrder componentsSeparatedByString:#"<"] objectAtIndex:0];
}
}
return result;
}
What do you do with the object that's returned by this method? [arrRetrive objectAtIndex:0] is likely what's being leaked.
Leaks indicates that line with componentsSeparatedByString: because that's where the strings in the array are allocated. But the array isn't leaked.
Go into Leaks, look at a leaked instance, and click that little arrow button. You'll see all the retains and releases of the leaked object, which should point you to the problem.