Understanding 'potential null dereference' when dealing with NSError - objective-c

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.

Related

Safe Casting, but EXC_BAD_ACCESS anyway

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.

Casting an NSError return to a CFErrorRef return

I have a function that returns an NSError object by reference:
NSData *foo(NSData *foo, NSError *__autoreleasing *outError);
This function uses an API that takes a pointer to storage for a CFErrorRef. I'd like to just pass outError to the underlying function directly, but I can't figure out the correct combination of declaration keywords and cast keywords to make clang agree with that plan. Is there one?
If you look at the clang notes for __autoreleasing it mentions that the magic happens at assignment time, which means that automatic casting can't help here. You need to actually make the assignment using a temporary variable as mentioned in the comments on the original post.
Try this:
NSError+CFErrorRef.h
#interface NSError (CFErrorRef)
- (CFErrorRef) cferror;
#end
NSError+CFErrorRef.m
#import "NSError+CFErrorRef.h"
#implementation NSError (CFErrorRef)
- (CFErrorRef) cferror
{
CFStringRef domain = (CFStringRef) self.domain;
CFDictionaryRef userInfo = (__bridge CFDictionaryRef) self.userInfo;
return CFErrorCreate(kCFAllocatorDefault, domain, self.code, userInfo);
}
#end
I wrote a quick little unit test to verify everything converted over and it appears to be working correctly. Then you can just perform the following selector on your NSError object
CFErrorRef e = [error cferror];

Concept behind NSError

I have been using NSError in a few places but I don't really grasp the concept behind it. Especially why it is used using a double pointer like this:
NSError *err = nil;
NSString *s = nil;
s = [NSString stringWithContentsOfURL:url error:&err];
So why can't we just pass the variable errto the method, which is passed as "by-reference" anyway as far as I can tell.
Thanks for some clarification
This is one of the best descriptions around about NSError.
Actually your are passing not an object but a pointer to a pointer. With this, you can set your NSError before you send a message to an object to nil and only if an error occurs, your NSError will "suddenly" hold an value so that you can do stuff like this:
NSError *err = nil;
[SomeClass doSomethingGreatWhichCanFailWithError:&err];
if(err) {
NSLog("Something failed :-(");
//TODO: Add better logging!
}
Otherwise you would have to create an instance of NSError before you send the message and than check for a property or something else if an error occured.
Passing an object by reference would require you to allocate and initialize an NSError object that can then be manipulated by the called method to suit the needs. This also requires to have all funcitonality either directly in NSError or require the caller to know of and use specific subclasses of NSError.
Using this method the caller does not have to create an NSError object at all. In most cases no error should occur and there is no creation of useless error objects. Additionally any class could feature a subclass of NSError to give further information. If the caller does not care about this, it interprets the returned object as simple NSError and everything is fine. If the caller is interested in details, it can cast to the subclass and get further information.

What are the best practices for exceptions/returning NO/nil in Objective-C?

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 .

How to assign a value to an pointer-pointer passed in call by reference?

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. :)