I'm trying to get the hang of objective-c and how to use dictionaries. I tried a small example, but it keeps crashing for me. Here is my dictionary and an attempt to loop the dictionary keys.
NSDictionary *dictionary = #{
#"anObject" : #"#hej",
#"helloString" : #"Hello, World!",
#"magicNumber" : #42,
#"aValue" : #33
};
for(NSString *key in dictionary) {
NSLog(#"key:%#", key);
NSLog(#"value:%#", [[dictionary valueForKey:key] string]);
}
It crashes with a Thread 1: signal SIGABRT
Terminating app due to uncaught exception >'NSInvalidArgumentException', reason: '-[__NSCFConstantString string]: >unrecognized selector sent to instance 0x1078590a8'
Here on the return line:
int main(int argc, char * argv[]) {
#autoreleasepool {
return UIApplicationMain(argc, argv, nil,
NSStringFromClass([AppDelegate class]));
}
Updated
NSLog() will call the [NSObject description] method of any object you pass it, so this will work fine for you:
NSLog(#"value:%#", dictionary[key]);
There is no string method, so that's why you are getting the unrecognized selector exception.
Related
I am implementing a Mac App and I want to handle following events:
Unhandled Exception
Program Crash (memory error dcc)
If I detect them, I can send details to me to analyze and fix bugs using one of the Crash Handlers that I found. Alas I am unable to figure out how to intercept crashes and exceptions.
First question: Have I to differentiate Exceptions from Crashes ? Or detecting Exception is enough?
How can I catch Exceptions and/or crashes redirecting them to my handler ?
PS
I tried following in my MyApp class
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
but it doesn't work. Every time it crashes, it goes to debugger without classing SignalHandler or uncaughtExceptionHandler
I have found the best way is to create a simple Exception handling delegate class as this allows exceptions in IBAction methods to be caught.
main.mm:
#interface ExceptionDelegate : NSObject
#end
static ExceptionDelegate *exceptionDelegate = nil;
int main(int argc, char **argv)
{
int retval = 1;
#autoreleasepool
{
//
// Set exception handler delegate
//
exceptionDelegate = [[ExceptionDelegate alloc] init];
NSExceptionHandler *exceptionHandler = [NSExceptionHandler defaultExceptionHandler];
exceptionHandler.exceptionHandlingMask = NSLogAndHandleEveryExceptionMask;
exceptionHandler.delegate = exceptionDelegate;
//
// Set signal handler
//
int signals[] =
{
SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV,
SIGSYS, SIGPIPE, SIGALRM, SIGXCPU, SIGXFSZ
};
const unsigned numSignals = sizeof(signals) / sizeof(signals[0]);
struct sigaction sa;
sa.sa_sigaction = signalHandler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
for (unsigned i = 0; i < numSignals; i++)
sigaction(signals[i], &sa, NULL);
....
}
....
return retval;
}
static void signalHandler(int sig, siginfo_t *info, void *context)
{
logerr(#"Caught signal %d", sig);
exit(102);
}
#implementation ExceptionDelegate
- (BOOL)exceptionHandler:(NSExceptionHandler *)exceptionHandler
shouldLogException:(NSException *)exception
mask:(unsigned int)mask
{
logerr(#"An unhandled exception occurred: %#", [exception reason]);
return YES;
}
- (BOOL)exceptionHandler:(NSExceptionHandler *)exceptionHandler
shouldHandleException:(NSException *)exception
mask:(unsigned int)mask
{
exit(101);
// not reached
return NO;
}
#end
You'll need to add the ExceptionHandling.framework to your project.
I wrote a small CLI program to delete specific Safari cookies for me. Functionally it's fine, but it's throwing up warnings about objects being "autoreleased with no pool in place". My project has ARC enabled, hence why I don't have any autorelease pools.
Here's my code:
// NSLog replacement from http://stackoverflow.com/a/3487392/1376063
void IFPrint (NSString *format, ...) {
va_list args;
va_start(args, format);
fputs([[[NSString alloc] initWithFormat:format arguments:args] UTF8String], stdout);
fputs("\n", stdout);
va_end(args);
}
int main(int argc, const char * argv[])
{
NSString *urlSearchString;
if (argc > 1) {
urlSearchString = [[NSString alloc] initWithUTF8String:argv[1]];
}
else {
IFPrint(#"No URL provided, quitting.");
return 1;
}
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSString *filterString = [[NSString alloc] initWithFormat:#"domain ENDSWITH '%#'", urlSearchString];
NSPredicate *filter = [NSPredicate predicateWithFormat:filterString];
NSArray *matchedCookies = [cookieStorage.cookies filteredArrayUsingPredicate:filter];
for (int i = 0; i < matchedCookies.count; i++) {
[cookieStorage deleteCookie:[matchedCookies objectAtIndex:i]];
}
IFPrint(#"Removed %li cookies", matchedCookies.count);
return 0;
}
The message I get is:
objc[15502]: Object 0x107b2bf00 of class NSThread autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
Which appears in the Xcode debugger or when running the release binary directly (slight digression: shouldn't these messages be stripped out of the "release" build?). The line that causes it seems to be:
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
Similarly, if I run it without passing an argument, I get a similar message:
objc[15630]: Object 0x100114ed0 of class __NSCFString autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
objc[15630]: Object 0x100114f80 of class __NSCFData autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
Which appears to come from the IFPrint function I'm using (however this doesn't show up when I use the IFPrint when I provide a proper argument).
I'm a bit out of my depth here, can anyone show me where (and how) I've gone wrong?
ARC still requires an autorelease pool. Methods like [NSPredicate predicateWithFormat:filterString] continue to release an autoreleased object (though you no longer need to concern yourself all that much since ARC handles it). Furthermore the internal implementation of any library method you call may create arbitrarily many autoreleased objects while running.
You should wrap your code in an autorelease pool via the #autoreleasepool mechanism.
Wrap the entire body of main with #autoreleasepool like so:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
// your code
}
}
All you need to do is add an autoreleasepool in your main.
int main(int argc, const char * argv[])
{
#autoreleasepool
{
//Your code
}
}
Using Xcode (4.3.3 and llvm), I set a breakpoint on all exceptions but often when the breakpoint is hit, the stack looks like this:
(lldb) bt
* thread #1: tid = 0x1c03, 0x31a891c4 libobjc.A.dylib`objc_exception_throw, stop reason = breakpoint 1.1
frame #0: 0x31a891c4 libobjc.A.dylib`objc_exception_throw
frame #1: 0x33a677b8 CoreFoundation`+[NSException raise:format:arguments:] + 100
There's no information about the caller of NSException raise:format:arguments:, the stack just stops there.
Something must have called raise so what happened to the stack? What should I look for to help me debug such issues (e.g. does this indicate some specific type of stack corruption or something else)?
Update: here's the code with the garbage.
The problem was that I accidentally wrote %s when I meant %#. Might have been a bit of a stretch to call that a properly-allocated string but I could "po imageName" in the llvm console, it just had some garbage at the end.
NSString *imageName = [NSString stringWithFormat:#"award_%s", award];
UIImage *image = [UIImage imageNamed:imageName];
[_awardIconMapping setObject:image forKey:award];
There was no thread #0 by the time the exception was hit. Using #try/#catch, I get a sensible exception from the last line (sorry, I thought it was the image= line before):
2012-07-06 10:43:36.184 CallVille[834:707] ****** Hit exception ********
-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: wiseGuy)
I use this function to print the stack traces from a NSException:
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
+ (void) printStackTrace: (NSException*) e
{
NSArray * addresses = [e callStackReturnAddresses];
if( [addresses count])
{
void * backtrace_frames[[addresses count]];
int i = 0;
for (NSNumber * address in addresses)
{
backtrace_frames[i] = (void *)[address unsignedLongValue];
i++;
}
char **frameStrings = backtrace_symbols(&backtrace_frames[0], [addresses count]);
if(frameStrings != NULL)
{
int x;
for(x = 0; x < [addresses count]; x++)
{
NSString *frame_description = [NSString stringWithUTF8String:frameStrings[ x]];
NSLog( #"------- %#", frame_description);
}
free( frameStrings);
frameStrings = nil;
}
}
}
I am following the Stanford CS193P class and I am trying to do assignment 2. I have now for hours stared at the same error message:
-[NSCFString stringValue]: unrecognized selector sent to instance 0x4b373e0,
while trying endless revisions of the code
The method running is this one:
+ (double)evaluateExpression:(id)anExpression
usingVariableValues:(NSDictionary *)variables;
{
CalculatorBrain *evalBrain =[[CalculatorBrain alloc]init];
for (id element in anExpression) {
if([element isKindOfClass:[NSString class]]) {
NSString *elementFirst=[element substringToIndex:1];
if ([elementFirst isEqual:#"v"])
{
NSString *varLookUp = [element substringFromIndex:0];
[evalBrain setOperand:[[variables objectForKey:varLookUp]doubleValue]];
}
else
{
[evalBrain performOperation:element];
}
}
if ([element isKindOfClass:[NSNumber class]]) {
[evalBrain setOperand: [element doubleValue]];
}
}
return [evalBrain operand];
}
it is at the statement
if ([elementFirst isEqual:#"v"])
the program crashes.
Grateful any hints!
Try
if ([elementFirst isEqualToString:#"v"])
instead.
Here is result of NSLog on elementfirst:
2011-02-06 16:00:11.554 Calculator[14160:207] elementfirst: +
it correctly shows that I hit the '+' key which is not equal to 'v' and the program should go to the else block. Unfortunately it crashes in stead...
Hey. I am reading in a string from a file and attempting to use the resulting string to set a BOOL property on an object using the KVC method -setValue:forKeyPath:. However, this breaks with an exception: -[NSCFString charValue]: unrecognized selector sent to instance 0x7fff711023b0. I'm guessing this is because BOOL is typedef'd from char. Is there a way around this? Thanks!
When setting a BOOL property using KVC, you need to pass an NSNumber object. What you could do in your case is pass [NSNumber numberWithBool:[myString boolValue]]. That should fix your crash.
I am catching the exception, checking it's name, and then retrying with a wrapped value when needed. Here is the code:
#try
{
[(NSObject*)retObj setValue:[[obj keyValuePairs] objectForKey:key]
forKeyPath:key];
}
#catch (NSException * e)
{
if ([[e name] isEqualToString:NSInvalidArgumentException])
{
NSNumber* boolVal = [NSNumber numberWithBool:[[[obj keyValuePairs] objectForKey:key] boolValue]];
[(NSObject*)retObj setValue:boolVal
forKeyPath:key];
}
}
Thanks anyway!
Add a simple category to your project:
#implementation NSString (CharValue)
- (BOOL)charValue {
return [self isEqualToString:#"0"] ? NO : YES;
}
#end