This is more of a C question but here it goes.
I've a method that receives as a parameter the address of a pointer to an NSError object. Now, that method is buried several levels deep in the class hierarchy and I need to make the error object bubble all the way to the top.
I could return the error object on each method but I'd rather do it the Cocoa way and return a boolean while passing the error object as a parameter.
How can I do this?
I could return the error object on each method but I'd rather do it the Cocoa way and return a boolean while passing the error object as a parameter.
The Cocoa way is the Boolean direct return with a by-reference (i.e., through pointer) return of the error value, like so:
NSError *error = nil;
if ([foo trySomething:bar error:&error]) {
//Success!
} else {
//Failure!
}
(Alternatively, trySomething:error: may return an object, in which case you treat that object as the Boolean return: non-nil is true/succeeded, nil is false/failed.)
To make this chainable, each method (except the outermost) should have an error-pointer parameter, and use that in its implementation:
- (void) trySomething:(MyBar *)bar error:(out NSError **)outError
if ([bartender restock:bar error:outError]) {
//Success!
} else {
//Failure!
}
}
You can combine both approaches, catching the error object in your own local variable in order to customize or wrap it in the failure case before storing the customized/wrapper error at the error-return pointer for your caller to receive.
Related
So I have something like :
- (void) printString:(NSString *)string
{
NSLog(#"%#", string);
}
NSString *string = #"Blach";
[self printString:string];
Would string be passed in by value or reference? What about something like NSError?
NSError *error = [NSError errorWithDomain:someDomain
code:-101
userInfo:someInfo];
-(NSString *) doSomething:(BOOL) val withError:(NSError **)error {
if(!val)
{
*errorPtr = [NSError errorWithDomain:something
code:-101
userInfo:someInfo];
}
}
You can never refer to an Objective-C object by anything other than a pointer, so for all intents and purposes, everything is pass-by-reference. That said, the actual pointer values are passed by value, just like in C.
Everything of every type in Objective-C is pass-by-value only.
The question is invalid because "objects" are not values in Objective-C and therefore cannot be "passed" -- you cannot have a variable or expression whose value "is an object" (a bare object type is not allowed in the language) -- instead, "objects" must always be manipulated behind pointers to objects. Since "objects" are not values and you cannot "pass" them, it is meaningless to ask about whether they are "pass-by-value" or "pass-by-reference".
Objects are passed by reference. The important distinction, to my mind, is whether the called method has a reference to the same object as the caller or if it has a copy. It has a reference to the same object.
If the object is mutable and the called method mutates it, it affects the object that the caller has, too (because it's the same object).
In the case of an NSError** parameter, it is actually the pointer (not an object) which is being passed by reference. A method with such a parameter can actually modify the caller's pointer, making it point to a different object.
I have some block that get data from server. I could see that I get error when trying to return my array from that block. Question is, what is right :
use delegates from the block to the other class that use it
don't use block,but selector method that being called when done, and I give it argument that is being filled and return.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for(PFObject *object in objects)
{
PFObject *user = [object objectForKey:#"userId"];
//can't return value to other class here ..
}
}];
Should I use delegate here? (is it possible?)
The typical Cocoa way to do this would be to use a delegate with a callback. If you use selectors instead, you will still need to pass the block an instance of the object that the selector belongs to, and you lose some compile-time safety.
You can simply declare a variable or a property assignable in block with __block prefix and then you can assign a value to it in the block and use everywhere you need.
Example:
__block NSString *string;
Obj-C blocks are something I'm just using for the first time recently. I'm trying to understand the following block syntax:
In the header file:
#property (nonatomic, copy) void (^completionBlock)(id obj, NSError *err);
In the main file:
-(void)something{
id rootObject = nil;
// do something so rootObject is hopefully not nil
if([self completionBlock])
[self completionBlock](rootObject, nil); // What is this syntax referred to as?
}
I appreciate the assistance!
Blocks are Objects.
In your case inside the method you are checking if the block is not nil and then you are calling it passing the two required arguments ...
Keep in mind that blocks are called in the same way a c function is ...
Below i have split the statement in two to let you understand better :
[self completionBlock] //The property getter is called to retrieve the block object
(rootObject, nil); //The two required arguments are passed to the block object calling it
Its a block property, you can set a block at runtime.
Here is the syntax to set
As it is void type, so within the class you can set a method by following code
self.completionBlock = ^(id aID, NSError *err){
//do something here using id aID and NSError err
};
With following code you can call the method/block set previously.
if([self completionBlock])//only a check to see if you have set it or not
{
[self completionBlock](aID, nil);//calling
}
I realize that this is similar to an existing post here, What's the Point of (NSError**)error?, but my question is a little different. I understand how the double pointer works, and how this is the common iOS API error pattern. My question is more around the single pointer, and why this code doesn't work:
- (BOOL)someMethodWithError:(NSError *)error
{
...
if( errorOccured )
{
NSError *e = [[[NSError alloc] initWithDomain:#"" code:1 userInfo:nil] autorelease];
error = e;
return NO;
}
return YES;
}
implemented using:
NSError *error = nil;
if( ![someObj someMethodWithError:error] )
{
NSLog(#"Error: %#", [error localizedDescription]);
}
Why doesn't the assignment in the method implementation reassign the pointer to the new NSError object?
I find that it helps to think of a pointer as just an integer. (That's what it is.)
Look at your example with an int.
-(BOOL)someMethodWithError:(int)error
{
error =100;
return NO;
}
That integer is pass by value. after that function is called error will not change.
error = 123;
[self someMethodWithError:error];
//error is = 123;
The pointer is the same thing. It's pass by value.
NSError * error; //this assigns this to a value in memory.
NSLog(#"%p",error);
[self someMethodWithError:error];
NSLog(#"%p",error); // the value of error will not have changed.
if you want the pointer to change you need to send in a pointer to that pointer and change it. It is confusing but draw yourself a diagram of memory and think about it. :)
It's all about the double pointer.
Why isn't the new value showing? Because you never modified the contents of the object which you initialized as nil. To do that you would have needed to use a reference to the memory location where the nil value was stored (a pointer to the pointer), not just a copy of the nil value (which is what you are passing using the single pointer).
This is a really common thing to do in C where some of the output of a function is "returned" through a pointer in the input arguments.
This is how the C language works. You might like to take a look at this question and answer:
C double pointer, which is basically the same thing in a different guise.
Well error that somemethodWithError has is different pointer which is containing the address of the error from where someMethodWithError is get called. so when you assign any object to error pointer of someMethodWithError it will not reflect in the error pointer of method from where it is called
it's like
NSError * error1 = obj1;
NSError * error2 = error1;
and afterward if you assign any new object to error2 like
error2 = obj2;
it will not change the pointer value of error1 and it will still point to obj1
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 .