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.
Related
My program:
- (void)getPostPraiseListWithSessionId:(NSString *)sid withPostId:(NSString *)postId withPageNo:(int)pageNo completeBlock:(void (^)(NSError *))complete{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{
#"sid":sid,
#"post_id":postId,
#"pageNo":[[NSNumber alloc] initWithInt:pageNo],
};
[manager POST:[NSString stringWithFormat:#"%#%#",K_BASE_URL,K_GET_POST_PRAISE_LIST_URL] parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
if([[responseObject objectForKey:#"state"] intValue] == 0){
NSError *error = [NSError new];
NSArray *postPraiseList = [MTLJSONAdapter modelOfClass:MSMPostPraiseInfo.class fromJSONDictionary:[responseObject objectForKey:#"data"] error:&error];
complete(error);
}else{
NSError *error = [[NSError alloc] initWithDomain:MSMNETWORK_ERROR_DOMAIN code:[[responseObject objectForKey:#"state"] intValue] userInfo:#{#"message":[responseObject objectForKey:#"message"]}];
complete(error);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
complete(error);
}];
}
I have some questions.I wanna pass one parameter to the block named completeBlock,but I don't know what type of the parameter should I use.Should I use the weak type or the strong type?Whatever the type is weak or strong,please tell me the reason.
Rob explained it a lot better, but long story short you use weak when you don't care much about the object being valid, allowing it to be dealloced before your block use it. strong should be used when the object must be valid (and in memory) to be used inside your block.
Also, here are some minor improvements you could do (which not really related to your question):
[[NSNumber alloc] initWithInt:pageNo] can be replaced by a simple #(pageNo)
If K_BASE_URL and K_GET_POST_PRAISE_LIST_URL are declared in #define as NSString* macros, you don't need to use stringWithFormat:, you can simply use [manager POST: K_BASE_URL K_GET_POST_PRAISE_LIST_URL parameters:(...)]
In this case, your NSError *error = [NSError new]; would probably better off as NSError *error = nil;, but it depends on what that method of MTLJSONAdapter does.
Frequently you will use a weak reference because you often do not want the request to maintain a strong reference to the object in question. For example, if you're updating a view object in your user interface, if the view is dismissed before the request finishes, there's no benefit in having the network request keeping a strong reference to a user interface control that is no longer visible.
Sometimes, when dismissing a view controller, it is useful to cancel any pending network requests that are only being used for the benefit of that scene. In that situation, you would want to save the reference to the AFHTTPRequestOperation object returned by POST method and then cancel that request in dealloc of the view controller. But that only works if you explicitly avoid having the blocks maintain a strong reference back to the view controller, i.e. you used weak references.
In your case, you would appear to be posting data, so maybe canceling the request isn't necessary, so this latter point might not be relevant. But, nonetheless, you may well still use weak references if you're only referring to UI controls that may be dismissed while the request is running. But if you're calling some additional instance methods that are updating a persistent store with the result of the request upon completion of the request, then you might need strong reference.
Bottom line, the basic question is whether it is critical for the object in question to be retained for the duration of the network request (i.e. the completion block would have a strong reference) or not. Generally you don't need that, and would thereby favor weak reference. But we can't speak to your specific case without more information about what you plan on doing within the closure you pass to getPostPraiseListWithSessionId method. The choice of strong or weak is not a function of the getPostPraiseListWithSessionId method, but rather a question of what you plan on doing inside the completion block you supply to this method.
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;
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 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];
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 .