Reputation: 528
I have the following code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:SOMEURL];
NSError *err;
NSData *response = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:nil error:&err];
if (err)
{
// Do some error stuff
}
err = nil;
NSDictionary *downloadedDictionary = [NSJSONSerialization JSONObjectWithData:response options:0 error:&err];
if (err)
{
// Do some error stuff
}
dispatch_sync(dispatch_get_main_queue(), ^{
NSDictionary* item = [downloadedDictionary objectForKey:@"user"];
NSString* name = [item valueForKey:@"name"];
[txtName setText:name];
});
});
However I crash every time on [txtName setText:name]. txtName is set in the header and synthesized properly. In fact, if I do [txtName setText:@"Random text"] it works just fine. And I can also do NSLog(@"%@", name) and it will log the information just. It's just when I try to mix the two together that it crashes. Any ideas? My error is
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI length]: unrecognized selector sent to instance 0x109c66bd0'
Upvotes: 1
Views: 361
Reputation: 41632
Try this, and make sure you have a Xcode breakpoint on exceptions:
dispatch_sync(dispatch_get_main_queue(), ^{
assert(store);
assert([store isKindOfClass:[NSDictionary class]]);
NSString* name = [store valueForKey:@"name"];
assert(name);
assert([name isKindOfClass:[NSString class]]);
assert([self.txtName isKindOfClass:[UITextField class]]);
[self.txtName setText:name]; // note the user of "self"
});
Upvotes: 1
Reputation: 100612
You've done the correct thing there by hopping back onto the main queue in order to update your UIKit objects. As you've presumably noticed from the documentation, UIKit isn't generally safe for use on background threads or queues, subject to a few exceptions that Apple mentions in a fairly disorganised ad hoc fashion.
That being said, it appears that [store valueForKey:@"name"]
is an array, not a string. So your mistake is failing to validate and/or correctly to parse your returned JSON. If you NSLog
it (or, if you want to be really sure, [name class]
) you should see that.
Similarly if you inspect the JSON you'll probably see square brackets around the result.
You probably want something like:
NSString *name = nil; // or don't bother with "= nil" if using ARC
NSArray *names = [store valueForKey:@"name"];
if([names isKindOfClass:[NSArray class]] && [names count] > 0)
name = names[0];
if(name)
[txtName setText:name];
Assuming you are expecting exactly one name. The two if
s there act as validation, and Objective-C exactly follows the C rule for conditional evaluation — if the isKindOfClass:
fails then the call to count
will never occur, so it's safe to test for type on one side of a conditional and then assume it on the other.
Upvotes: 0