Pointer to Error-Pointer in Objective-C - objective-c

Sorry, if found a lot of threads like the but they were not about this Error**-thing.
I tried to 'design' my methods like the error-examples I found. But calling the second, the error is not pointing to nil, the debugger says error: summary string parsing error.
This is my controller-method:
-(void) refresh {
NSError *error;
ServerApi *serverApi = [mainModel newServerApi];
NSArray *newItems = [serverApi getNewItems: &error];
...
This is the called method:
- (NSArray *) getNewItems: (NSError **) error {
// Breakpoint here, error is: 'error: summary string parsing error'
...
NSURLResponse *response;
NSData *responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: error];
I thought, I did the same as Apple with sendSynchronousRequest.... Their comment tells
error Out parameter (may be NULL) used if an error occurs
while processing the request. >>>>Will not be modified if the
load succeeds.<<<<
What did I do wrong and why does this work for Apples sendSynchronousRequest...?

The code is fine, as error does not need to be initialized "from outside".
Also, if you use ARC it will automatically initialize local object pointers to nil, so
NSError *error;
is no different than
NSError *error = nil;
under ARC.
While explicit initialization is still a good practice, that's not the source of any error here.
That being said,
summary string parsing error
is a lldb error. My hypothesis is that it gets confused by the double pointer, but I wouldn't worry too much.
By the way, you're doing a slight mistake in implementing this pattern.
Synchronous methods that may fail, should method to return a BOOL value indicating whether the computation was successful and then clients will check that value and subsequently inspect the error object in case it failed.
Checking the error object is in general a bad idea: even some Apple APIs can fail and yet return a nil error, so avoid doing that!

Remember to set your pointer to nil in refresh:
NSError *error = nil;
Also, remember, your checks should be:
Checking (*error) for nil (aka "there was no error passed") - operation may or may not have been successful;
Checking error for NULL (aka "there was no NSError* pointer passed, so don't assign it an object").

Related

data parameter is nil when i trying to fetch the data from web service using json

- (NSDictionary*)PostWebService:(NSString*)completeURL param:(NSString*)value
{
#try
{
NSString *urlStr =[completeURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
//NSString *urlPart=#"req=value";
//NSString *urlPart;
NSString *urlPart=[NSString stringWithFormat:#"req=%#", value];
NSLog(#"String %#",urlPart);
NSData *requestBody = [urlPart dataUsingEncoding:NSUTF8StringEncoding];
//NSLog(#"String %#",requestBody);
[request setHTTPBody:requestBody];
NSURLResponse *response = NULL;
NSError *requestError = NULL;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&requestError];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] ;
NSLog(#"String %#",responseString);
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
return json;
}
#catch(NSException *e)
{
NSLog(#"reason is%#",e.reason);
}
}
and i call this method here..
-(NSDictionary*)gotvall:(NSString*)req
{
#try
{
NSString *vurl=#"some url/";
// vurl=[vurl stringByAppendingString:#"req="];
// vurl=[vurl stringByAppendingString:req];
// NSLog(#"%#",vurl);
NSDictionary *json=[self PostWebService:vurl param:req];
NSLog(#"json is%#",json);
return json;
}
#catch(NSException *e)
{
NSLog(#"%#",e.reason);
}
}
After debugging this method I got result as data parameter is nil.
Can anyone tell that what I am doing wrong here.
I got the complete url and when I run that url on browser I got the perfect data but when I printing the value of json it returns null.
You reported that your error was:
Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo=0x8d84960 {NSErrorFailingURLStringKey=%20http://www.hugosys.in/www.nett-torg.no/api/vehicl‌​e/, NSErrorFailingURLKey=%20http://www.hugosys.in/www.nett-torg.no/api/vehicle/, NSLocalizedDescription=unsupported URL, NSUnderlyingError=0x8d8bfe0 "unsupported URL"}
That %20 in your error message is a space at the start of your URL that your stringByAddingPercentEscapesUsingEncoding call converted to %20. If you remove that extra space, you should be in good shape.
A couple of other observations:
Just to warn you, your use of stringByAddingPercentEscapesUsingEncoding will correctly handle the presence of a space in the req value. But it will not properly handle the presence of certain other characters (notably & or +). If it's possible that those sorts characters might appear in the req value, you might want to remove the call to stringByAddingPercentEscapesUsingEncoding for the whole URL, and instead just use CFURLCreateStringByAddingPercentEscapes (which gives you a little more control over the percent escaping process) on just the req value. See the percentEscapeString method in this answer: Append data to a POST NSURLRequest.
In both your network request as well as your JSON parsing process, you are returning an NSError object. I might suggest that you log those values if they're ever non-nil, which will help you diagnose problems in the future.
I notice that you are using exception handling. That's not common in Objective-C. As the Programming in Objective-C guide says:
Dealing with Errors
Almost every app encounters errors. Some of these errors will be outside of your control, such as running out of disk space or losing network connectivity. Some of these errors will be recoverable, such as invalid user input. And, while all developers strive for perfection, the occasional programmer error may also occur.
If you’re coming from other platforms and languages, you may be used to working with exceptions for the majority of error handling. When you’re writing code with Objective-C, exceptions are used solely for programmer errors, like out-of-bounds array access or invalid method arguments. These are the problems that you should find and fix during testing before you ship your app.
All other errors are represented by instances of the NSError class. This chapter gives a brief introduction to using NSError objects, including how to work with framework methods that may fail and return errors. For further information, see Error Handling Programming Guide.
Bottom line, As I mentioned in the second point, you should be checking NSError return values yourself rather than relying on exceptions in Objective-C.

How to initialize, pass argument, and check error condition using NSError**

Xcode 4.3
I've read the SO questions on NSError**, so I wrote a simple test program that uses a slightly different syntax recommended by Xcode 4.3 (see __autoreleasing below), so I'm not 100% sure if this is correct, although the code does appear to function properly. Anyway, just a simple file reader, prints an error if the file can't be found.
Questions
Would like to know if the NSError initialization, argument passing using &, and error condition checking are correct.
Also, in the readFileAndSplit.. method, I noticed a big difference between if(!*error) and if(!error), in fact, if(!error) does not work when no error condition is raised.
File Reading Method w/Possible Error Condition
-(NSArray*) readFileAndSplitLinesIntoArray:(NSError *__autoreleasing *) error {
NSString* rawFileContents =
[NSString stringWithContentsOfFile:#"props.txt"
encoding:NSUTF8StringEncoding
error:error
NSArray* fileContentsAsArray = nil;
if(!*error)
fileContentsAsArray =
[rawFileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
return fileContentsAsArray;
Caller
SimpleFileReader* reader = ...
NSError* fileError = nil;
NSArray* array = [reader readFileAndSplitLinesIntoArray: &fileError];
if(fileError){
NSLog(#"Error was : %#, with code: %li",
[fileError localizedDescription],(long)[fileError code]);
}
There are a couple of issues.
First, As per Apple's Error Handling Programming Guide, you should be checking a method's return value to determine whether a method failed or not, and not NSError. You only use NSError to get additional error information in the event that the method failed.
E.g.:
NSArray* fileContentsAsArray = nil;
NSString* rawFileContents = [NSString stringWithContentsOfFile:#"props.txt"
encoding:NSUTF8StringEncoding
error:error];
if (rawFileContents)
{
// Method succeeded
fileContentsAsArray = [rawFileContents ...];
}
return fileContentsAsArray; // may be nil
Second, NSError out parameters are typically optional and may be NULL. But if you pass a NULL error variable into your method it will crash on this line:
if (!*error) {
because you're dereferencing a NULL pointer. Instead, you must always check for NULL before referencing a pointer, like so:
if (error && *error)
{
// Do something with the error info
}
However, if you rewrite the method as indicated above then you won't be accessing the error variable at all.

NSError EXC_BAD_ACCESS

I need a second pair of eyes on why I'm getting an error when trying to assign the NSError to the one passed into the function:
// Response and Error Objs.
NSURLResponse *response = nil;
NSError *requestError = nil;
// Attempt authentication
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&requestError];
// Error?
if (requestError != nil) {
*error = requestError; // Error happens here
return;
}
From Apple's Error Handling Programming Guide
Important: Success or failure is indicated by the return value of the
method. Although Cocoa methods that indirectly return error objects in
the Cocoa error domain are guaranteed to return such objects if the
method indicates failure by directly returning nil or NO, you should
always check that the return value is nil or NO before attempting to
do anything with the NSError object.
In this case, -sendSynchronousRequest:returnResponse:error: returns an NSData object. You should check to see if that is nil before proceeding with the error.
Did you forget the NSError:
if (requestError != nil) {
    NSError *error = requestError; // Error happens here
    return;
}
Or where/when is error declared/defined and whats the point of assigning one error objet to another? Why not just:
if (requestError != nil) {
    NSLog(#"%#", [requestError localizedDescription]);
    return;
}
Most likely *error doesn't point to a valid pointer; the sample isn't complete, so I can't say for certain. Perhaps *error is nil.
you can't use 'error' in the form you are trying to use. You can access error in NSURLConnection but not outside it.
Hence you need to define your error object before using it.
Lets say you have a method signature that accepts an NSError**.
This means that the method accepts a pointer to a pointer that is pointing to an NSError value.
So, I suspect because error is NULL, your basically asking that NULL object what the value is of the thing that it's pointing to. But of course, you cannot ask a NULL object for that information, that's why it crashes.
So, before you try to set the value of *error, you need to check that error is not NULL like so:
if (error == NULL) {
// the function caller has supplied us with a nil as an argument
// this indicates the caller does not care about the error
return;
}
The reason you have to check for NULL instead of nil is because you are checking a memory location not a cocoa object.

NSError: Does using nil to detect Error actually turn off error reporting?

I got into the habit of coding my error handling this way:
NSError* error = nil;
NSDictionary *attribs = [[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error];
if (error != nil) {
DLogErr(#"Unable to remove file: error %#, %#", error, [error userInfo]);
return;
}
But looking at the documentation It seems like I got this wrong.:
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
If an error occurs, upon return contains an NSError object that describes the problem. Pass NULL if you do not want error information.
Technically there is no difference between nil and NULL so does this mean I'm actually turning this off and will never get a error message (even if the delete in the above example did fail) ?
Is there a better way to code this ?
Thanks.
First off, the following line doesn't really make sense:
NSDictionary *attribs = [[NSFileManager defaultManager]
removeItemAtPath:fullPath error:&error];
-removeItemAtPath:error: returns a BOOL value, not a dictionary.
I think I see what you’re wondering about with the NULL value. Notice carefully though, how there are 2 *'s in the error parameter in the method signature:
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
That means a pointer to a pointer. When you pass in &error, you are passing in the address of the pointer to the NSError. (Ugh, someone else can probably help me out here, as my head still starts to swim when dealing with pointers to pointers). In other words, even though you have set error to nil, you aren't passing in error to the method, you're passing in &error.
So, here’s what the re-written method should look like:
// If you want error detection:
NSError *error = nil;
if (![[NSFileManager defaultManager] removeItemAtPath:fullPath
error:&error]) {
NSLog(#"failed to remove item at path; error == %#", error);
// no need to log userInfo separately
return;
}
// If you don't:
if (![[NSFileManager defaultManager] removeItemAtPath:fullPath
error:NULL]) {
NSLog(#"failed to remove item at path");
return;
}
Passing NULL means the following:
BOOL itemRemoved = [[NSFileManager defaultManager] removeItemAtPath:fullPath
error:NULL];
i.e., the error parameter is NULL. Internally, -removeItemAtPath:error: sees if a valid pointer was passed. If it’s NULL, it simply won’t report the error as an NSError instance — but the return value will indicate whether the method completed successfully.
Also, your test is wrong. You shouldn’t be using the error output parameter to detect if an error occurred because it might be set even if the method completes successfully. Instead, you should use the return value of the method to detect errors. If the return value (in this particular case) is NO, then use the error output parameter to get information about the error:
NSError *error = nil;
BOOL itemRemoved = [[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error];
if (itemRemoved == NO) {
DLogErr(#"Unable to remove file: error %#, %#", error, [error userInfo]);
return;
}
Quoting the Error Handling Programming Guide,
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
Edit: As NSGod pointed out, -removeItemAtPath:error: returns BOOL, not NSDictionary *. I’ve edited my answer to reflect that as well.
No I do it the same way and it works just fine for detecting errors. You are not passing NULL to it you are passing a pointer to NULL to it which is a very different thing. Although another option you might want to add is.
if (error != nil){...
}else{
[NSApp presentError:error]
}

fails, but no error object is returned. Why?

I'm trying to understand the meaning of the value returned by [NSData writeToFile:options:error:]. The method returns a BOOL, which according to Apple's documentation is "YES if the operation succeeds, otherwise NO."
Fair enough, but if it's NO, I would have assumed that the error parameter would then be set to some retrievable NSError* value. However in results I'm coming across, that's not the case. Accordingly I'm somewhat confused, and don't know how to determine what caused the failure.
To wit, I've got this code (more or less):
NSError* error = nil;
BOOL success = [data writeToFile: filePath error: &error];
if ( error )
NSLog( #"error = %#", [error description] );
success turns out to be NO in the code I'm running, but the NSLog statement is never executed. How come?
Howard
It's possible that data is nil, in which case [data writeToFile:error:] returns nil, but *error is not set.
The writeToFile method returns TRUE on success and FALSE on failure -that's what you want to actually check for.
So, Try:
if(!success)
As your conditional instead of if( error ).