Disclaimer: I'm quite new to Obj-C and iOS (5, ARC enabled).
The following implementation of an NSURLConnectionDelegate method creates EXC_BAD_ACCESS in the NSLog call inside the if:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Response %#", response );
if([response isKindOfClass:[NSHTTPURLResponse class]])
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
NSLog(#"HTTP status code %#", [httpResponse statusCode]);
}
}
As far as I managed to find out, the EXC_BAD_ACCESS is caused mostly due to allocation issues, wrong casting, and bad memory management. None of that applies here (I hope).
Thanks in advance,
Chris
Solution: Noobie error in formatting the og string. Change the second NSLog to:
NSLog(#"HTTP status code %i", [httpResponse statusCode]);
statusCode returns an NSInteger (a long or an int), not a pointer to an NSObject instance.
The format specifier %# is used for NSObjects arguments. The problem likely occurs when the integer value that is returned from statusCode is interpreted/passed as a pointer to an object and then messaged or otherwise treated as a pointer to an object by the runtime. When an object argument is printed via %# the logger uses the result of the object's -[NSObject description].
You can avoid this problem in the future by turning up your compiler warnings and correcting the issues it generates.
Related
I got an exception that says:
-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance
Is it saying I am trying to access an NSNull object with a key?
Any idea what causes this and how to fix it or debug further?
The way to fix it is to not attempt objectForKeyedSubscript on an NSNull object. (I'm betting you're handling some JSON data and aren't prepared for a NULL value.)
(And apparently objectForKeyedSubscript is what the new array[x] notation translates into.)
(Note that you can test for NSNull by simply comparing with == to [NSNull null], since there's one and only one NSNull object in the app.)
What ever value you are storing, despite what the editor tells you, at run time you are storing an NSNull, and later on trying to call objectForKeyedSubscript. I am guessing this happening on what is expected to be an NSDictionary. Some thing like:
NSString *str = dict[#"SomeKey"]
Either a piece of code beforehand is not doing its job and investigate there, or perform some validation:
NSDictionary *dict = ...;
if ( [dict isKindOfClass:[NSDictionary class]] ) {
// handle the dictionary
}
else {
// some kind of error, handle appropriately
}
I often have this kind of scenario when dealing with error messages from networking operations.
I suggest adding a category to NSNull to handle this in the same way you would expect a subscript call to be handled if it it were sent to nil.
#implementation NSNull (Additions)
- (NSObject*)objectForKeyedSubscript:(id<NSCopying>)key {
return nil;
}
- (NSObject*)objectAtIndexedSubscript:(NSUInteger)idx {
return nil;
}
#end
A simple way to test is like this:
id n = [NSNull null];
n[#""];
n[0];
With this category, this test should be handled successfully/softly.
I understand that this may seems a very basic question, but I always thought that explaining (and understanding) basic question is the key to build clean and working code.
I have the following fragment of code in a CoreData entity:
-(BOOL)validateForInsert:(NSError *__autoreleasing *)error {
[super validateForInsert:error];
return [self validateInternal:error];
}
-(BOOL)validateInternal:(NSError *__autoreleasing *)error {
// checking attributes and build userInfo error
if(anyErrors) {
*error = [NSError errorWithDomain:appDomain code:200 userInfo:details];
return NO;
}
return YES;
}
by running 'analyze' in Xcode I get the famous 'potential null dereference' when creating error. While the compiler is not giving any alert, and the code has always worked.
This could be a question too, why the compiler doesn't warn me about that ?
But coming to the code, I perfectly know the solution, which is to check if (error != nil), but I am quite lost at this.
The question start at the beginning, at the meaning of NSError *_autoreleasing* , why the surrounding star ? Which should be a pointer to a pointer ?
Supposing I would like to call the validateForInsert: by myself, how can I build an *_autoreleasing* object ?
The following question, I suppose is related to the question above: if I am building the *error from scratch, why am I suppose to check if nil first ?
Last but not least, the code works fine, the errors get intercepted, could you please spot a case that can have it fail or crash ? As I said I am using CoreData, a generic example would be fine, but another related to CoreData is appreciated.
thanks
*error = [NSError errorWithDomain:appDomain code:200 userInfo:details];
is syntactically correct, therefore the compiler does not warn. The Analyzer detects that this will crash at runtime if error = NULL, i.e. if you call
[myObj validateForInsert:NULL];
If you call
NSError *error;
[myObj validateForInsert:&error];
then you pass the address of error, therefore you pass a variable of type NSError * *.
The __autoreleasing modifier is a hint to the ARC compiler that the object assigned to error is an autoreleased object.
You can declare a variable to be autoreleasing with
NSError * __autoreleasing error;
[myObj validateForInsert:&error];
but normally you do not care. ARC just generates the correct code.
For details, read the Transitioning to ARC Release Notes. There are also lots of answers on this topic on stackoverflow.
I'm new to Objective-C, and I see that there are different conventions used about error handling. There are exceptions, but also there are situations where functions are just supposed to return nil in case of something going wrong.
So, how do I decide when use which, and how to handle exceptions and unexpected return values? What are the best practices and red flags?
I won't be definitive about which to use, but here's some info about each of the options:
Exceptions
Exceptions in Obj-C are not really meant to be used to control program flow. From the documentation on exception handling:
The general pattern is that exceptions are reserved for programmer error only, and the program catching such an exception should quit soon afterwards.
For this reason I wouldn't recommend using exceptions #try/#catch just to test whether a method worked correctly.
You also have several options for handling exceptions, in addition to setting a higher-level uncaught exception handler.
Errors
Errors are typically used in three ways:
Delegate methods
An object can simply pass an NSError to its delegate in a designated error-handling callback:
- (void)myObject:(MyObject *)obj didFailWithError:(NSError *)error;
The delegate is then free to take any appropriate action, including perhaps displaying a message to the user. This pattern is commonly used in asynchronous delegate-based APIs.
Out parameters
These are most commonly used in conjunction with a boolean return value: if the return value is NO, then the NSError object can be examined for more information about the error.
- (BOOL)performTaskWithParameter:(id)param returningError:(out NSError **)error;
Where one possible usage pattern would be:
NSError *error;
if (![myObject performTaskWithParameter:#"param" returningError:&error]) {
NSLog(#"Task failed with error: %#", error);
}
(Some people also prefer to store the boolean result in a variable before checking it, such as BOOL success = [myObject perform...];.) Due to the linear nature of this pattern, it's best used for synchronous tasks.
Block-based completion handlers
A fairly recent pattern since the introduction of blocks, yet a quite useful one:
- (void)performAsynchronousTaskWithCompletionHandler:(void (^)(BOOL success, NSError *error))handler;
Used like this:
[myObject performAsynchronousTaskWithCompletionHandler:^(BOOL success, NSError *error) {
if (!success) {
// ...
}
}];
This varies a lot: sometimes you won't see the boolean parameter, just the error; sometimes the handler block has no arguments passed to it and you just check a state property of the object (for example, this is how AVAssetExportSession works). This pattern is also great for asynchronous tasks, when you want a block-based approach.
Handling errors
Cocoa on Mac OS X has a quite thorough error-handling path. There is also NSAlert's convenience method + (NSAlert *)alertWithError:(NSError *)error;. On iOS, the NSError class still exists, but there aren't the same convenience methods to handle errors. You may have to do a lot of it yourself.
Read the Error Handling Programming Guide for more information.
Returning nil
This is often used in conjunction with NSError out parameters; for example, NSData's method
+ (id)dataWithContentsOfFile:(NSString *)path
options:(NSDataReadingOptions)mask
error:(NSError **)errorPtr;
If reading the file fails, this method returns nil, and further information is stored in an error.
One reason this is a particularly convenient pattern is because of nil messaging, which can be done safely with no effect in Obj-C. I won't go into detail here on why this is useful, but you can read more about it elsewhere on the interwebs. (Just make sure to find an up-to-date article; it used to be that methods returning floating-point values wouldn't necessarily return 0 when sent to nil, but now they do, as described in the documentation.)
Exceptions should be used as little as possible in Objective-C. Where other languages would use exceptions, in Objective-C it's recommended to make use of NSError objects most of the time.
Apple's documentation on exception handling is here: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Exceptions/Exceptions.html%23//apple_ref/doc/uid/10000012il
So how would one make use of NSError objects? Well, if we look at Apple's classes, errors are returned using an indirection pointer.
For example:
- (NSObject *)objectFromSet:(NSSet *)set error:(NSError **)error
{
// get an object from a set; if the set has at least 1 object
// we return an object, otherwise an error is returned.
NSObject *object = [set anyObject]
if (!object)
{
*error = [NSError errorWithDomain:#"AppDomain" code:1000 userInfo:nil];
return nil;
}
return object;
}
// and then we use the function like this
- (void)test
{
NSError *error = nil;
NSSet *set = [[[NSSet alloc] init] autorelease];
NSObject *object = [self objectFromSet:set error:&error];
if (object)
{
// use the object, all went fine ...
}
else
{
// handle error, perhaps show an alert view ...
}
}
If a method is supposed to return an object, and it is unable to do so, it should return nil. If there's an error that you want to report to the user so that they can take some kind of action about it, use an NSError object.
Objective-C supports exceptions in much the same way as other programming languages, with a similar syntax to Java or C++. As with NSError, exceptions in Cocoa and Cocoa Touch are objects, represented by instances of the NSException class,
You can use
#try {
// do something that might throw an exception
}
#catch (NSException *exception) {
// deal with the exception
}
#finally {
// optional block of clean-up code
// executed whether or not an exception occurred
}
Show more about Error Handling apple doc .
Is it "better style" to send a message and hope the object responds, or to check to see if it responds to a selector and have some sort of fallback if it doesn't.
For example:
- (NSString *)stringForObjectValue:(id)obj {
if ([obj respondsToSelector:#selector(intValue)]) {
NSString *roman = [self formatRomanNumber:[obj intValue] resultSoFar:#""];
return roman;
} else {
return [NSString stringWithFormat:#"can't format a %#", [obj class]];
}
}
vs.
- (NSString *)stringForObjectValue:(id)obj {
NSString *roman = format_roman(#"", [obj intValue]);
return roman;
}
(the example is from a NSNumberFormatter subclass...but it could be from a NSObjectFormatter subclass...)
If you're not 100% sure that all instances that come to your (stringForObjectValue) function respond to selector then you must perform that check to avoid crashes in runtime.
How to handle the cases when obj does not respond to intValue selector may depend on particular context where your method is used. For example you may return nil object from method in that case so you can easily see that something went wrong
If you don't know the exact type then using respondsToSelector: is definitely good style, because you risk an exception otherwise. And it's so important that there's a name for this technique: Duck Typing.
I want to achieve something similar to what these guys do here:
- (NSUInteger)countForFetchRequest:(NSFetchRequest *)request error:(NSError **)error
like you can see, you pass an NSError pointer and that nice method will assign a real NSError object to your pointer in case there is an error. So the cool thing about this is, that the method returns an NSUInteger but can ALSO return an NSError, without having to mess around with ugly and fat arrays or dictionaries.
So how could I assign an object to the passed-in error pointer?
It's easy. This Apple guide shows how you might implement a method that returns an NSError object. But to make a long story very, very short:
- (NSUInteger)countForFetchRequest:(NSFetchRequest *)request error:(NSError **)error
{
//Do stuff, including make MyCustomErrorDomain and errCode and eDict.
if (error != NULL) { // check to avoid crash if **error is not provided
*error = [[[NSError alloc] initWithDomain:MyCustomErrorDomain code:errCode userInfo:eDict] autorelease];
}
//Do some more stuff.
}
Note the asterisk. :)