I am trying to understand the fundamentals of blocks. I wrote this simple test:
NSString *(^print_block) () = ^ (NSString *returned_string){
return #"this block worked!";
};
NSLog(#"%#", print_block);
I expected console output to be "this block worked!", but instead I get a big flood of error numbers and etc,, ending with:
terminate called throwing an exception
What up?
Edit: the answer has been suggested to use:
NSLog (#"%#", print_block());
But that doesn't work either. The program terminates at the start of the block definition, with the console saying only (lldb) and Xcode putting a little green arrow at the block definition. The arrow reads:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x5f646e71)
I've tried something else that doesn't work:
NSString *(^print_block) () = ^ (NSString *returned_string){
NSString *return_me = #"this block worked!";
return return_me;
};
NSLog(#"%#", print_block);
But at least this doesn't terminate the program. It runs fine. But the console output is still wrong:
<__NSGlobalBlock__: 0x5a58>
Vatev's comment is right on. When you write:
NSLog(#"%#", print_block);
you're passing the block print_block as the argument for the format string in the log statement. You're trying to print the block. This probably results in [print_block description] being called. I don't know if blocks implement a -description method, but if not then you'll get an unrecognized selector exception.
Also, the way you've declared the block is incorrect. You don't need to include the return value in the parameter list.
The following code works as you expect:
NSString *(^print_block)() = ^{
return #"this block worked!";
};
NSLog(#"%#", print_block());
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 was hoping for some help to understand why I need to re-cast my variable when it is a string from the start.
Here's the code:
+ (BOOL)hasOperandComponents:(NSString *)operandToTest
{
NSArray *componentsOfOperand = [[NSString stringWithFormat:#"%#",operandToTest] componentsSeparatedByString:#" "];
if (componentsOfOperand.count>1) return YES; return NO;
}
If I don't use the embedded call to 'stringWithFormat' then I get the rather common error:
-[__NSCFNumber componentsSeparatedByString:]: unrecognized selector sent to instance
I have been able to find answers to what this error means and hence how to avoid it (see my code above) by searching other Q's and A's... but no good explanation as to why my operandToTest seems to 'forget' that it is a NSString and become an _NSCFNumber.
I'm suspicious that it is because this is a Class method... but why would that matter when a specific instance of NSString *operandToTest is passed to the class method?
Please help?
The problem is you don't have a string to begin with. The value that you're passing into the method as operandToTest is, in fact, an NSNumber*. You need to look at the calling function to figure out why this is.
Objective-C's runtime seems to be rather robust, so I was wondering if there's a way to log the name of the function that called the current function (for debugging purposes).
My situation is that a bunch of things assign to a property, and rather than set a breakpoint and examine the call stack each time, I'd like to just NSLog the name of the function that is setting the property, along with the new value.
So is it possible to get access to the call stack at runtime?
Try this:
#include <execinfo.h>
void *addr[2];
int nframes = backtrace(addr, sizeof(addr)/sizeof(*addr));
if (nframes > 1) {
char **syms = backtrace_symbols(addr, nframes);
NSLog(#"%s: caller: %s", __func__, syms[1]);
free(syms);
} else {
NSLog(#"%s: *** Failed to generate backtrace.", __func__);
}
Great Question. Combining Jeremy's Answer above and what we always use for our debugging, you'll get a nice string like this:
NSArray *syms = [NSThread callStackSymbols];
if ([syms count] > 1) {
NSLog(#"<%# %p> %# - caller: %# ", [self class], self, NSStringFromSelector(_cmd),[syms objectAtIndex:1]);
} else {
NSLog(#"<%# %p> %#", [self class], self, NSStringFromSelector(_cmd));
}
There is no facility for getting the sender. Or, at least, nothing centric to Objective-C.
There are a couple of alternatives, though.
First, you could use GDB commands. Go to the GDB console and do something like:
comm 1
bt
po argName
cont
Alternatively, you could use dtrace. This is hard. Or you could use Instruments which makes dtrace somewhat easier.
Or you could use the backtrace() function. See the backtrace man page (x-man-page://backtrace).
There is a C macro called __PRETTY_FUNCTION__ that will return a C-String with the name of the current function. If you would like to convert that to an NSString for easily printing to NSLog, you can create a macro with
#define NSSTRING_PRETTY_FUNCTION [NSString stringWithCString:__PRETTY_FUNCTION__ encoding:NSASCIIStringEncoding]
I use it all the time in my projects.
in my code, I need to compare two strings to see if they are equal. if they are it needs to preform a function. one of the strings is just a #"someString", the other is part of an object.
if ([[[mine metal] stringValue] isEqualToString:#"Gold"])
{
//some function
}
however there are some complications when I do this. first, it gives me a warning: NSString may not respond to -stringValue. and when I run the Application it quits out at the if statement: the console reports " -[NSCFString stringValue] : unrecognized selector sent to instance." mine.metal is defined through a fast enumeration loop across an array; the metal attribute is defined as an NSString, and NSLog is able to display this string. what else am I missing?
The compiler warning and the subsequent run-time error both tell you what the problem is.
[mine metal] returns an NSString. NSString doesn't have a method called stringValue.
If [mine metal] does indeed return an NSString then you can do this:
if ([[mine metal] isEqualToString:#"Gold"])
{
//some function
}
I am getting a EXC_BAD_ACCESS (SIGBUS) on this line in my iPhone project:
if (timeoutTimer) [timeoutTimer invalidate];
The thing that has me stumped is that I don't understand how that line could crash, since the if statement is meant to be checking for nil. Am I misunderstanding the way Objective-C works, or do line numbers in crash statements sometime have the wrong line in them?
Just because a variable is set to a value other than nil doesn't mean it's pointing to a valid object. For example:
id object = [[NSObject alloc] init];
[object release];
NSLog(#"%#", object); // Not nil, but a deallocated object,
// meaning a likely crash
Your timer has probably already been gotten rid of (or possibly hasn't been created at all?) but the variable wasn't set to nil.
I just ran into a similar issue, so here's another example of what might cause a check such as yours to fail.
In my case, I was getting the value from a dictionary like this:
NSString *text = [dict objectForKey:#"text"];
Later on, I was using the variable like this:
if (text) {
// do something with "text"
}
This resulted in a EXC_BAD_ACCESS error and program crash.
The problem was that my dictionary used NSNull values in cases where an object had an empty value (it had been deserialized from JSON), since NSDictionary cannot hold nil values. I ended up working around it like this:
NSString *text = [dict objectForKey:#"text"];
if ([[NSNull null] isEqual:text]) {
text = nil;
}
They should be the same. Perhaps the line number is in fact incorrect.
Look for other possible errors near that in your code and see if you find anything.