NSInvalidArgumentException, nil argument - objective-c

I've looked over the site for a while and I saw a few things but nothing worked for me. Extreme newbie here though.
I'm trying to make a 2 component picker, populated from arrays, populated from a dictionary, populated from a plist. The components populate, and I have a button that spins one component. The second button is meant to check answers (the first component has states which they try to match with capitals in the second component), but this is where it always crashes.
The error I get is as follows:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSPlaceholderString initWithFormat:locale:arguments:]: nil argument'
* First throw call stack:
(0x14b3022 0xeb3cd6 0x145ba48 0x145b9b9 0x916169 0x916ab4 0x3036 0x14b4e99 0x1914e 0x190e6 0xbfade 0xbffa7 0xbf266 0x3e3c0 0x3e5e6 0x24dc4 0x18634 0x139def5 0x1487195 0x13ebff2 0x13ea8da 0x13e9d84 0x13e9c9b 0x139c7d8 0x139c88a 0x16626 0x27ad 0x2715)
terminate called throwing an exception(lldb)
I'm not sure where I'm going wrong. I've done this with hard coded arrays, and it works fine. The code for the button is like so (please ignore the switch code, I haven't quite figured out how to make that work yet, I want the screen to turn green for correct answers and red for incorrect, but I'm more concerned with the app crashing!):
-(IBAction)checkAnswer;
{
NSInteger StateRow = [pickerCapital selectedRowInComponent:karrayStateComponent];
NSInteger CapitalRow =[pickerCapital selectedRowInComponent:karrayCapitalComponent];
NSString *state = [arrayState objectAtIndex:StateRow];
NSString *capital = [arrayCapital objectAtIndex:CapitalRow];
NSLog (#"%i",[pickerCapital selectedRowInComponent:karrayCapitalComponent]);
NSLog(#"%i", [pickerCapital selectedRowInComponent:karrayStateComponent]);
NSString *Title = [[NSString alloc]initWithFormat:#"You have selected %# as the capital of %#.", capital, state];
NSString *Message =[[NSString alloc]initWithFormat:lblMsg];
UIAlertView *alert =[[UIAlertView alloc]initWithTitle:Title message:Message delegate:#"" cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
if (StateRow == CapitalRow) {
lblMsg = #"You are correct!";
UIColor *myColor;
switch ([not sure what to use here]) {
case 0:
myColor = [UIColor greenColor];
break;
default:
break;
}
}
else {
lblMsg = #"Incorrect";
UIColor *myColor;
switch ([not sure what to use here]) {
case 0:
myColor = [UIColor redColor];
break;
default:
break;
}
}
}
If anyone can offer any sort of help on this, I would appreciate it greatly! Thank you!

Two things to check:
Where is lblMsg declared or assigned a value?
Are you sure capital and state are not nil from the calls to [arrayCapital objectAtIndex]?
Specifically: the NSString documentation states "Important: Raises an NSInvalidArgumentException if format is nil." for initWithFormat.

Related

Crash on second address book (contacts) lookup

Update:
I believe the problem lies somewhere in how I am storing the reference to ABRecordRef. I am currently just hanging onto the value as delivered to peoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier: and not CFRetaining it or anything. It's unclear from the documentation if it needs to be retained.
I'm working on an iPhone app and it interfaces with the address book using the AddressBook and AddressBookUI frameworks. I'm using the ABPeoplePickerNavigationController to present a contact list to the user to choose, and am capturing the resultant ABRecordRef as an instance variable on a custom class.
This is all working fine on the first use. However, the second time I pick someone from the contacts (even a different person), my app blows up with EXC_BAD_ACCESS on a call to ABRecordCopyValue. I am logging the pointers and they are definitely different each time a contact is selected (even if the same contact twice).
I fail to understand how this reference could be deallocated. A memory leak sure, but why does it work fine the first time and not the second?
Here's the actual call it's dying on:
- (NSString*)displayName {
return CFBridgingRelease( ABRecordCopyValue( self.contact, kABPersonFirstNameProperty ) );
}
Here's some debug output if it's helpful at all:
Printing description of self->_contact:
(ABRecordRef) _contact = 0x1f582dc0
(lldb) expr (CFTypeRef)ABRecordCopyValue(self.contact, kABPersonFirstNameProperty)
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0xd1f57cc1).
The process has been returned to the state before execution.
Turns out all I needed was to CFRetain( person ) and everything's happy go lucky again. I also added a dealloc to my class to clean up the pointer when the object goes away:
- (void)dealloc {
CFRelease( _contact );
}
My code now runs smoothly and the static analyser is happy (not that it caught the leak anyway).
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
[self displayPerson:person];
[self dismissViewControllerAnimated:YES completion:nil];
return NO;
}
you returning NO ?
Try checking to see if the value exists maybe
ie
- (void)displayPerson:(ABRecordRef)person
{
NSString* companyName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonOrganizationProperty);
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString* display = #"";
if (companyName) {
display = companyName;
} else if (name) {
display = name;
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"No Details For Contact"
message:#"Please update contact with company and/or first name"
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
}

NSString cannot be released

Consider the following method and the caller code block. The method analyses a NSString
and extracts a "http://" string which it passes out by reference as an auto release object.
Without releasing g_scan_result, the program works as expected. But according to non-arc rules, g_scan_result should be released since a retain has been called against it.
My question are :
Why g_scan_result cannot be released ?
Is there anything wrong the way g_scan_result is handled in the posted coding below ?
Is it safe not to release g_scan_result as long as the program runs correctly and the XCode Memory Leak tool does not show leakage ?
Which XCode profile tools should I look into to check and under which subtitle ?
Hope somebody knowledgeable could help.
- (long) analyse_scan_result :(NSString *)scan_result target_url :(NSString **)targ_url {
NSLog (#" RES analyse string : %#", scan_result);
NSRange range = [scan_result rangeOfString:#"http://"
options:NSCaseInsensitiveSearch];
if (range.location == NSNotFound) {
*targ_url = #"";
NSLog(#"fnd string not found");
return 0;
}
NSString *sub_string = [scan_result substringFromIndex : range.location];
range = [sub_string rangeOfString : #" "];
if (range.location != NSNotFound) {
sub_string = [sub_string substringToIndex : range.location];
}
NSLog(#" fnd sub_string = %#", sub_string);
*targ_url = sub_string;
return [*targ_url length];
}
The following is the caller code block, also note that g_scan_result has been declared and initialized (on another source file) as :
NSString *g_scan_result = nil;
Please do send a comment or answer if you have suggestions or find possible errors in code posted here (or above). Xcode memory tools does not seem to show any memory leak. But it may be because I do not know where to look as am new to the memory tools.
{
long url_leng = [self analyse_scan_result:result target_url:&targ_url];
NSLog(#" TAR target_url = %#", targ_url);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Scanned Result"
message:result
delegate:g_alert_view_delegate
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
if (url_leng) {
// ****** The 3 commented off statements
// ****** cannot be added without causing
// ****** a crash after a few scan result
// ****** cycles.
// ****** NSString *t_url;
if (g_system_status.language_code == 0)
[alert addButtonWithTitle : #"Open"];
else if (g_system_status.language_code == 1)
[alert addButtonWithTitle : #"Abrir"];
else
[alert addButtonWithTitle : #"Open"];
// ****** t_url = g_scan_result;
g_scan_result = [targ_url retain];
// ****** [t_url release];
}
targ_url = nil;
[alert show];
[alert release];
[NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(activate_qr_scanner:)
userInfo:nil
repeats:NO
];
return;
}
I think the mystery has been solved. Thanks for all who are kind enough to have looked into this. The reason is before adding the UIAlertView, coding had been done (on another source file) to assign a raw output string to g_scan_result and display it directly on the current view. So when g_scan_result got released, it is releasing the wrong NSString assigned by some outdated code.
In summary, the wrong NSString got released which is the source of the problem.
The solution is just to remove a single outdated statement. The statement from the old implementation was left there as I thought it wouldn't do any harm (and may even helped to make the variable populated continuously). But it turned out to be a very silly mistake. The only excuse is having very little sleep lately. Being able to find an excuse does serve a purpose. Just hope that it doesn't have to be done very often ...

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}

Change the page after an action is done

I have 2 pages with xib files. One is a login page and the other is a welcome page.
Here is the login function:
-(IBAction)login{
NSLog(#"login begin");
NSString *loginResponse = [self tryLogin];
NSLog(#"%#", loginResponse);
loginError.text = loginResponse;
NSUserDefaults *session = [NSUserDefaults standardUserDefaults];
[session setInteger:1 forKey:#"islogged"];
}
After the user is logged in successfully I need to redirect them to another xib file which is called Notifications. Now how exactly should I do this?
Also for some reason when I put an if statement inside the function, it doesnt go into the statements.
-(IBAction)login{
NSLog(#"login begin");
NSString *loginResponse = [self tryLogin];
NSLog(#"%#", loginResponse);
if(responseInt == #"2"){
NSLog(#"login error 2");
UIAlertView *alertURL = [[UIAlertView alloc] initWithTitle:#"Error!"
message:#"Username or password is wrong!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alertURL show];
[alertURL release];
///////////////////////////////////////////////////
}else if(responseInt == #"0"){
NSUserDefaults *session = [NSUserDefaults standardUserDefaults];
[session setInteger:1 forKey:#"islogged"];
}
}
As you can see I try to nslog something as soon as one of the conditions are met but it does not go into any of the statements (the loginResponse is sent back as 2).
How to change views
If you app is navigation-based then you can add new fullscreen views using method [self.navigationController pushViewController:controller animated:YES];.
Everywhere you can add new fullscreen view using next method : [self presentModalViewController:controller animated:YES];.
Issue with 'if-statement'.
When you want to compare to objects you should use one of following methods:
compare:, this method return one of following values: NSOrderedSame, NSOrderedDescending, NSOrderedAscending. For example, if ([responseInt compare:#"2"] == NSOrderedSame) {// equal}
'isEqual:', for example, if ([responseInt isEqual:#"2"]) {// equal}
or some specific comparators for concrete class
Don't forget that when you are comparing using == or > and etc you are comparing addresses of objects in memory.
Have you tried doing isEqualToString: instead of the equal sign? That is a more reliable string comparator. Also it seems strange that your login method is returning a string and not an integer. Is there a reason for that?

NSTable loses focus after present:error

I have an NSTableView that lists tags that are stored using Core Data. The default value for a tag is 'untitled' and I need each tag to be unique, so I have a validation routine that traps empty and non-unique values and that works fine. I don't want the user to be able to store the 'untitled' value for a tag, so I am observing the NSControlTextDidEndEditingNotification, which calls the following code:
- (void)textEndedEditing:(NSNotification *)note {
NSString *enteredName = [[[note userInfo] valueForKey:#"NSFieldEditor"] string];
if ([enteredName isEqualToString:defaultTagName]) {
NSString *dString = [NSString stringWithFormat:#"Rejected - Name cannot be default value of '%#'", defaultTagName];
NSString *errDescription = NSLocalizedStringFromTable( dString, #"Tag", #"validation: default name error");
NSString *errRecoverySuggestion = NSLocalizedStringFromTable(#"Make sure you enter a unique value for the new tag.", #"Tag", #"validation: default name error suggestion");
int errCode = TAG_NAME_DEFAULT_VALUE_ERROR_CODE;
NSArray *objArray = [NSArray arrayWithObjects:errDescription, errRecoverySuggestion, nil];
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedRecoverySuggestionErrorKey, nil];
NSDictionary *eDict = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
NSError *error = [[NSError alloc] initWithDomain:TAG_ERROR_DOMAIN code:errCode userInfo:eDict];
NSBeep();
[preferencesWindowsController presentError:error];
unsigned long index = [self rowWithDefaultTag];
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
// [self editColumn:0 row:index withEvent:nil select:YES];
}
}
- (unsigned long)rowWithDefaultTag {
__block unsigned long returnInt;
[managedTags enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj valueForKey:#"name"] isEqualToString:defaultTagName]) {
returnInt = idx;
*stop = YES;
}
}];
return returnInt;
}
With the 'editColumn' line commented out, the code works, so if the user accepts the default tag name without editing it, the error is built, displayed and the process finishes by leaving the appropriate row in the table highlighted.
However, I would like to take it that step further and place the user in edit mode. When I uncomment the 'editColumn' line, the behaviour is not at all what I expected - the tableView loses its blue focus box and the row that respresents the new tag is blank. If I click on the tableView, the row becomes visible. I've spent a lot of time on this and have got nowhere, so some help with this would be very much appreciated.
(Note: I tried using textDidEndEditing, which also didn't behave as I expected, but that is a separate issue!)
Answering my own question. Doh!
I already had a method which I used to put the user in edit mode when they clicked the button to add a new tag:
- (void)objectAdded:(NSNotification *)note {
if ([[note object] isEqual:self]) {
[self editColumn:0 row:[self rowWithDefaultTag] withEvent:nil select:YES];
}
}
Creating a notification to call this solves the problem and places the user in edit mode correctly. The important thing is not to try to do this on the existing runloop; so sending the notification as follows postpones delivery until a later runloop:
// OBJECTADDED is a previously defined constant.
NSNotification * note = [NSNotification notificationWithName:OBJECTADDED object:self];
[[NSNotificationQueue defaultQueue] enqueueNotification: note postingStyle: NSPostWhenIdle];
Problem solved. I wasted a lot of time trying to solve this - a classic example of getting too involved in the code and not looking at what I'm trying to do.
I've forgotten where I first saw this posted - whoever you are, thank you!