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
Related
I want to simulate the NSAlert member function which only exist on 10.9.
- (void)beginSheetModalForWindow:(NSWindow *)sheetWindow completionHandler:(void (^)(NSModalResponse returnCode))handler NS_AVAILABLE_MAC(10_9);
The code is as following:
-(void)compatibleBeginSheetModalForWindow: (NSWindow *)sheetWindow
completionHandler: (void (^)(NSInteger returnCode))handler
{
void *handlerValue = (__bridge void*) [handler copy];
[self beginSheetModalForWindow: sheetWindow
modalDelegate: self
didEndSelector: #selector(blockBasedAlertDidEnd:returnCode:contextInfo:)
contextInfo: handlerValue];
}
-(void)blockBasedAlertDidEnd: (NSAlert *)alert
returnCode: (NSInteger)returnCode
contextInfo: (void *)contextInfo
{
void(^handler)(NSInteger) = (__bridge typeof(handler)) contextInfo;
handler(returnCode);
[handler release];
}
I need to copy the handler before cast it to void*, else it will crash, if I change it to following line:
void *handlerValue = (__bridge void*) handler;
And by check the handler in blockBasedAlertDidEnd:returnCode:contextInfo:, it is an incorrect value.
Even I call [handler retain] before cast to void* it doesn't work.
It is quite confuse to me, so why I need to copy it?
This is because blocks are created on the stack and not the heap. So when the stack pointer moves on the memory in that stack frame is returned and the block is lost. Thats why you always should copy blocks and not retain them. When you run copy on a block the new copy is allocated on the heap and therefor don't get removed.
Peter Segerblom covered the basics of it. In the current implementation, there are 3 kinds of blocks. Blocks that don't capture local variables are global blocks; there is just one instance that has static lifetime. Blocks that do capture local variables start out as objects on the stack; and copying it will return a block on the heap. Copying heap or global blocks will simply return the same instance.
Basically, a block passed into your function could be a stack block or not. A stack block is only valid for the current function call. Since it could be a stack block, you must use a copy of it if you want to store it somewhere that outlives the current function call. This is the contract for functions that take a block parameter.
Now, if all your function does with this block is pass it to another function, do you need to pass a copy? If this function parameter is of block type, then no, because if this function needs to keep the block around for later, then it is responsible for copying it (according to the contract above). So you don't need to worry about it. But, if you pass it to a function that is not of block type (e.g. -[NSMutableArray addObject:]), then that function does not know to potentially copy it (it doesn't even know it's a block). In that case, you will have to pass a copy if that function keeps the object around for later. This is the case with the function you are passing to here.
I understand blocks are objective c objects and can be put in NSDictionary directly without Block_copy when using ARC. But I got EXC_BAD_ACCESS error with this code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self method1:^(BOOL result){
NSLog(#"method1WithBlock finished %d", result);
}];
}
- (void) method1:(void (^)(BOOL))finish{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:^(NSData *rcb){
finish(YES);
}, #"success",
^(NSError *error){
finish(NO);
}, #"failure", nil];
[self method2:dict];
}
- (void) method2:(NSDictionary *)dict{
void (^success)(NSData *rcb) = [dict objectForKey:#"success"];
success(nil);
}
If I change method1: to this, no error raised.
- (void) method1:(void (^)(BOOL))finish{
void (^success)(NSData *) = ^(NSData *rcb){
finish(YES);
};
void (^failure)(NSError *error) = ^(NSError *error){
finish(NO);
};
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:success, #"success",
failure, #"failure", nil];
[self method2:dict];
}
Can anybody explain why I have to use automatic variables to store the blocks before putting them to dictionary ?
I am using iOS SDK 6.1.
According to the "Transitioning to ARC Release Notes", you have to copy a block stored
in a dictionary (emphasis mine):
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 second method works "by chance" because success and failure are a
__NSGlobalBlock__ and not a "stack based block" that needs to be copied to the heap.
I understand blocks are objective c objects and can be put in NSDictionary directly without Block_copy when using ARC.
No, they're not common objects. When you create a block it is on the stack, and it doesn't matter of what is it's retain count, when you exit form the function it will be popped from the stack. Copy it to make it stay alive.
You must copy blocks before passing them to a method when 1) the block will be stored for longer than the duration of the call, and 2) the parameter you are passing it to is a normal object pointer type (i.e. id or NSObject *) instead of a block type.
This is the case for your call. dictionaryWithObjectsAndKeys: stores the argument in the resulting dictionary, and it simply expects normal object pointer arguments (of type id), and does not know whether you are passing blocks or not.
The reason for the second condition I said is because if the method parameter already takes a block type (e.g. for any completion handler parameters), then that method is already aware of the special memory management requirements of blocks, and therefore will take responsibility for copying the block if it needs to store it. In that case the caller doesn't need to worry about it. However, in your case, you are passing it to a general method that doesn't know it's getting a block, and thus doesn't know to copy it. So the caller must do it.
Can anybody explain why I have to use automatic variables to store the
blocks before putting them to dictionary ?
About this, your second example happens to work because recent versions of the ARC compiler is super conservative about blocks and inserts copies whenever you assign it to a block type variable. However, this is not guaranteed by the ARC specification, and is not guaranteed to work in the future, or in another compiler. You should not rely on this behavior.
i have a non arc project. i'm trying to use dispatch_async to get data from server and save it in sqlite. the dispatch_async happens inside a method with callback. on calling the method the app crashes with exc bad access. here is how i've implemented the code.
- (void) HandleData:(const char*) receivedData WithSuccess:(void(^)(BOOL finishing))completed
{
dispatch_queue_t fetchQ = dispatch_queue_create("Refreshing", NULL);
dispatch_async(fetchQ, ^{
[self write_data_in_sqlite]// **<--crash happens here in the method which is called here**
}
dispatch_sync(dispatch_get_main_queue(), ^{
completed(YES);
});
});
dispatch_release(fetchQ);
}
and i call the method as follow:
HandleResponse *handleResponse = [[[HandleResponse alloc] init] autorelease];
[handleResponse HandleData:aData WithSuccess:^(BOOL finishing) {
if(finishing)
{
//update the UI here
}
}];
if i remove the dispatch_async then it doesnt crash, but my UI gets blocked while writing to the sqlite.
what am i doing wrong?
edit:
removing the block and using dipatch_async produces the same exc_bad_access crash.
edit 2:
i tried example answer given below, it still crashes.
i thought to copy it then autorelease it. it crashes still but nit that often. i'm gonna check for memory leak. i'll report.
HandleResponse *handleResponse = [[[HandleResponse alloc] init] autorelease];
[handleResponse HandleData:aData WithSuccess: [[^(BOOL finishing) {
if(finishing)
{
//update the UI here
}
} copy] autorelease];
edit 3:
the crash happens in strlen even the xml content is in xmlResopnse. but why this happen with dispatch and not without it
xmlDocPtr xml= xmlParseMemory(xmlResopnse, strlen(xmlResponse);
edit 4:
as in answer below suggested not to use c objects in dispatch async. so i converted xmlResponse from const char* to nsstring and it doesnt crash.
Everything you've shown seems to be okay in terms of blocks and memory management. It must be something else.
I notice that you're passing in a C string (the char pointer receivedData) that you're not using. If you're not showing us the real code, and you are actually using the receivedData variable in the block, then that could be a problem, because the block simply captures the char pointer, but does not manage the memory of the string behind the pointer (it is not an Objective-C object). Therefore, it is possible that the C string is only valid in the calling scope (before the asynchronous operation), and no longer valid when the asynchronous operation runs. Your statement that something is crashing at strlen supports the idea that there is something wrong with some C string. You should try using NSString objects instead, since as objects they are properly memory-managed by blocks.
I have an Objective-C method that creates a pointer, then passes it to a new thread using performSelectorInBackground:. The problem is, I need to pass the address of the pointer to the thread because I want to free the memory associated with it from within the thread. The problem I'm having is that when the new thread is launched, the calling method ends, causing the pointer to go out of scope. How do I keep it around long enough where the thread can receive the pointer's address?
This all works of course if I simply call the "pointerTest" method on the main thread.
Here's my code:
-(void)pointerTest:(NSValue*)pointer
{
void **p = (void**)[pointer pointerValue];
fprintf(stderr,"p was %s\n",(char*)(*p)); //prints "Hello There!"
free(*p);
*p = NULL;
}
Now, on the main thread:
-(IBAction)doTest:(id)sender
{
char *str = "Hello There!";
void *p = malloc(strlen(str)+1);
strcpy(p, str);
//this works
//[self pointerTest:[NSValue valueWithPointer:&p]];
//this fails
[self performSelectorInBackground:#selector(pointerTest:) withObject:[NSValue valueWithPointer:&p]];
if(!p){
fprintf(stderr,"p was NULL!\n");
}else{
fprintf(stderr,"p was NOT NULL: %s\n",p);
}
}
Update:
This code shows that if I don't pass a pointer to pointerTest:, I cannot free p from within pointerTest:
-(void)pointerTest:(NSValue*)pointer
{
void *p = (void*)[pointer pointerValue];
fprintf(stderr,"p was %s\n",(char*)p); //prints "Hello There!"
free(p);
fprintf(stderr,"p was %s\n",(char*)p); //STILL prints "Hello There!"
*p = NULL;
fprintf(stderr,"p was %s\n",(char*)p); //prints "(null)"
}
Now, in the calling method:
//I pass p, NOT a pointer to p
[self pointerTest:[NSValue valueWithPointer:p]];
if(p){
//p is not NULL, and in fact I can print its value
fprintf(stderr,"p was NOT NULL: %s\n",p);
}
However, if I pass a pointer to p instead of p, I can free its value as expected from within pointerTest: and upon returning from pointerTest, p is NULL in the calling method.
[I won't ask why you're mixing malloc a C-strings with Objective-C, I guess you're experimenting. There is nothing wrong with doing so per se, it's just unusual in a situation like this.]
You don't need to pass a pointer to a pointer at all.
A pointer is the name of a box. Some boxes contain characters, some employee records, etc. and some box names (of other boxes). malloc finds a box of the needed size and returns its name, you can pass that around just like you would pass an integer value - you don't need to pass the name of the box containing the name of the box containing your characters...
Also when you malloc you need to allow for the trailing NUL character on a C string, so add 1 to the string length.
Try:
- (void) pointerTest:(NSValue *)pointer
{
void *p = (void *)[pointer pointerValue];
fprintf(stderr,"p (%p) was %s\n", p, (char *)p);
free(p);
}
- (IBAction) doTest:(id)sender
{
char *str = "Hello There!";
void *p = malloc(strlen(str)+1); // allow for NUL
fprintf(stderr,"p is %p\n", p);
strcpy(p, str);
// this works
// [self pointerTest:[NSValue valueWithPointer:p]];
// and so does this
[self performSelectorInBackground:#selector(pointerTest:) withObject:[NSValue valueWithPointer:p]];
if(!p)
{
fprintf(stderr,"p was NULL!\n");
}
}
Note you don't need to wrap p up in an NSValue either, nothing wrong with passing a pointer value to an Objective-C method.
HTH
After Comments
You are confusing the validity of a pointer with what might happen to be in the memory the pointer refers to.
The memory referred to by your pointer is freed. Your pointer value is no longer valid after the call to free - it is a dangling pointer.
The memory referred to by your, now dangling, pointer value may be re-used at any time. It just happens that at the point of your fprintf the memory has not yet been re-used and so you see the old contents - freeing memory does not clear it out.
Your store of NULL into the variable intends to remove your dangling pointer, doing so might be good practice in general but as you've found out taking the address of a short-lived variable and passing it to another method is a good way to create a dangling pointer. So you ended up trying to clean up a dangling pointer from box A to box B by using a dangling pointer to box A as box A had since been released itself... Let's try that a explaining that a different way ;-)
In this case you created a pointer to the local variable (box) p belonging to doTest, as soon as doTest completes the system returns the box that was being used for p to the free-box pile (or stack in this case) and your pointer became a dangling; when your original code for pointerTest then tried to store a NULL into that box via that, now dangling, pointer you hit a problem.
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).