NSError: passing error in method - objective-c

I'm not sure if this is correct or not:
- (void)parseSomething:(id)targetObject error:(NSError **)error {
NSError *parserError = nil;
[myParser parse:targetObject error:&parserError];
if (parserError != nil) {
*error = parserError;
}
}
the line:
*error = parserError;
I set error in parameter to be a local error, is it done correctly?
or should I do:
error = &parserError;
instead?

You need to make sure error isn't nil before you try to dereference it. Also, there is no need for the local NSError. It'd write that code this way:
- (void)parseSomething:(id)targetObject error:(NSError **)error {
[myParser parse:targetObject error:error];
}
But if you really wanted the locale variable (or for demonstration purposes). then this:
- (void)parseSomething:(id)targetObject error:(NSError **)error {
NSError *parserError = nil;
[myParser parse:targetObject error:&parserError];
if (error && parserError) {
*error = parserError;
}
}
Also, most methods that have an NSError out parameter like this usually have a BOOL return value or some other return value to indicate success or not. You shouldn't rely in the error parameter to indicate whether there was an error or not.
- (BOOL)parseSomething:(id)targetObject error:(NSError **)error {
NSError *parserError = nil;
BOOL ok = [myParser parse:targetObject error:&parserError];
if (error && parserError) {
*error = parserError;
}
return ok;
}

In this case you would probably want to pass the error further without intermediate variables. However, you need to be sure the myParser object handles the error parameter properly.
- (void)parseSomething:(id)targetObject error:(NSError **)error
{
[myParser parse:targetObject error:error];
}
In case you want to handle it here, you need to make sure the pointer to pointer error points to something, otherwise you will have crash when dereferencing it.
For example: the method was called like this
[object parseSomething:targetObject error:NULL];
The following line leads to crash:
*error = parserError;
Correct code would look like this:
- (void)parseSomething:(id)targetObject error:(NSError**)error
{
NSError* parserError = nil;
[myParser parse:targetObject error:&parserError];
if (error != nil)
{
*error = parserError;
}
}
Not a problem when parserError is nil, but when error is nil.

Related

NSDictionary get value as string does not complain but lenngth or isEqualToString crashes

I got a Json like below as a response message.
data:{
type = DATA;
version = "4.51";
}
Which i process in below method, from the debugger it looks like that version no is string.
-(void)processResponse:(NSDictionary *)responseDictionary{
NSString *version = [responseDictionary valueForKey:#"version"];
if([version isEqualToString:#"4.51"]){ //line 3
NSLog(#"do something"); //crashes
}
}
But line 3 crashes.
My question is why this line crashes? And what is the best way to handle this type of dictionary message?
If this is data that originates outside your program, you must always check the types.
-(void)processResponse:(NSDictionary *)responseDictionary {
id value = responseDictionary[#"version"];
if ([value isKindOfClass: [NSString class]]) {
NSString *version = value;
if ([version isEqualToString: #"4.51"]) {
NSLog(#"do something"); //crashes
}
}
}
If this is all you're going to do, then you can shorten it a little bit with && short-circuiting, if you're careful.
-(void)processResponse:(NSDictionary *)responseDictionary {
NSString *version = responseDictionary[#"version"];
if ([version isKindOfClass: [NSString class]]
&& [version isEqualToString: #"4.51"]) {
NSLog(#"do something"); //crashes
}
}
Just be very careful with this, because it means that version exists beyond this if block and may not be the correct type.
Again, if this is precisely your problem, you can also simplify it by using isEqual:, which applies to all NSObject:
-(void)processResponse:(NSDictionary *)responseDictionary {
id version = responseDictionary[#"version"];
if ([version isEqual: #"4.51"]) {
NSLog(#"do something"); //crashes
}
}

Creating NSError * results in application crash

I have the following situation:
My domain class gets some input validates it, and if validation passes it proceeds with saving data.
When control flow reaches the if statements, the application crashes
- (BOOL)createGmailAccountWithName:(NSString *)name
email:(NSString *)email
andPassword:(NSString *)password
error: (NSError **) error {
if (!name || name.length == 0) {
*error = [self createError:#"name"];
return NO;
}
if (!email || email.length == 0) {
*error = [self createError:#"email"];
return NO;
}
if (!password || password.length == 0) {
*error = [self createError:#"password"];
return NO;
}
//..
}
-(NSError *) createError: (NSString *) field {
NSString *errorMessage = [NSString stringWithFormat:#"Property %# is required", field];
NSDictionary *userInfo = #{
NSLocalizedFailureReasonErrorKey: NSLocalizedString(errorMessage, nil)
};
NSError *error = [NSError errorWithDomain:ACCOUNT_STORE_ERROR_DOMAIN
code:-1
userInfo:userInfo];
return error;
}
When I comment out all lines where the validation happens, the application does not crash.
I have no idea why this is happening. Can anyone point me into the right direction?
If you have this method:
- (BOOL)createGmailAccountWithName:(NSString *)name
email:(NSString *)email
andPassword:(NSString *)password
error: (NSError **) error
Folks are probably going to call it either like this:
NSError *error;
[accountCreator createGmailAccountWithName:#"Ben"
email:#"foo#example.com"
andPassword:#"pwd"
error:&error];
if (error)
{
NSLog(#"Hey I got an error: %#", error);
}
Or like this:
[accountCreator createGmailAccountWithName:#"Ben"
email:#"foo#example.com"
andPassword:#"pwd"
error:NULL];
// I couldn't care less about an error
In the second case, your code will will try to dereference **error, *error is not a valid pointer and would cause a crash.

Assigning data from an anonymous handler ios

I'm trying to get a reference of an NSArray that gets passed when I call sendAsyncrhonousRequest. Once I have that NSArray, I'd like to assign it to a class attribute but it seems I can't do that.
#implementation BarTableViewController {
NSArray *_jsonArray;
}
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode == 200 && data.length > 0 && error == nil)
{
NSError *e = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: &e];
if (!jsonArray) {
NSLog(#"Error parsing JSON: %#", e);
} else {
_jsonArray = jsonArray; // this doesn't work? _jsonArray is at the class level
}
}
else if (error)
{
NSLog(#"HTTP Status: %ld", (long)statusCode);
}
else if (statusCode != 200)
{
NSLog(#"HTTP Status: %ld", (long)statusCode);
}
}];
If I traverse jsonArray it will correctly display the data. If I assign it to _jsonArray to use it later, it no longer returns any data. The count of the array is zero.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _jsonArray.count; // always returns zero
}
How can I assign jsonArray to a class attribute so that I can use that data later?
My theory was correct. The UI was loading before the request finished. What I did to fix this issue was to call
[self.tableView reloadData];
Inside the async request.
Forget simple assignment:
#implementation BarTableViewController {
// NSArray *_jsonArray; forget it
}
instead own the object.
#interface BarTableViewController()
#property(nonatomic, strong)NSArray *jsonArray;
#end
/* --------- */
#implementation BarTableViewController
#syntethise jsonArray = _jsonArray;
#end
then to make it own it
self.jsonArray = jsonArray; // will call synthesized setter
I'm assuming you are using ARC, because you get a nil value instead of a dangling pointer. Now you will get a valid jsonArray.

UnitTesting: assertThat arguments incompatible type or pointer to integer without a cast

I am trying to set up my project TDD, so I've got the following unit test:
- (void)testOnDoesUsernameExistsShouldReturnFalseWhenInvalidJSONResponseFromService {
id mock = [OCMockObject partialMockForObject:(NSObject *) (id <ServiceHelperProtocol>) self.serviceHelper];
[[[mock stub] andReturn:#"invalid-json-response"] get:[OCMArg any]];
assertThat([self.signUpService doesUsernameExist:#"testusername"], true);
}
This test runs against this method:
- (BOOL)doesUsernameExist:(NSString *)userName {
NSString *url = [[NSString alloc] initWithFormat:#"%#/%#/api/signup.username_exists/?username=%#", kAPPLICATION_HOST, kAPPLICATION_NAME, userName];
NSString *responseString = [self.serviceHelper get:url];
if (responseString != nil && ![responseString isEqualToString:#""]) {
#try {
NSDictionary *dictionary = [responseString JSONValue]; // auto-released
NSString *usernameExists = [dictionary objectForKey:#"username_exists"];
return usernameExists != nil && ![usernameExists isEqualToString:#"null"];
}
#catch (NSException *e) {
NSLog(#"Unable to parse JSON: %#", e.description);
}
}
[responseString release];
[url release];
return false;
}
When I run this test, I get the following warning on line 94 (the assertThat line):
passing argument 2 of 'HC_assertThatWithLocation' makes pointer from integer without a cast
When I substitute the macro I get this line of code:
HC_assertThatWithLocation(self, [self.signUpService doesUsernameExist:#"testusername"], HC_is(0), "_file_name_", 0);
For some reason my test fails, but I can't figure out what is wrong. Is the test case wrong, is the implemented method wrong or does it has something to do with the OCMock framework in conjunction with the OCHamcrest matchers ?
I'm using Xcode 4.2 with both OCMock 1.66 and OCHamcrest 1.7.
Any thoughts ?

In Objective-C, I'm trying to encapsulate multiple error-able calls and "return" the most useful error

I put "return" in quotes because I don't want to literally return it. I want to do it similar to how you pass a pointer-to-a-pointer for [NSString stringWithContentsOfFile:usedEncoding:error:].
I would like to make parseFiles:error return nil and have the error reference that was passed in contain the first or second error, depending on which one failed. It seems like a Cocoa way to do it?
EDIT: Sorry, I should've been more clear about where I was having the problem. If the first path is bogus, it functions as I want. (I get the error instance outside and it prints.) If the first path is legit, as it filler string below implies, I get EXC_BAD_ACCESS.
But now I fixed it. I need to refer to it as *error inside the parseFiles:error: method and use == nil when checking if it failed. I thought I could just to if (error)...
EDIT 2 Ok, it doesn't work. I'm getting EXC_BAD_ACCESS. I'm not sure what I'm doing wrong with the conditions that check for the errors.
#implementation PassingError
- (id)init {
self = [super init];
NSError *error;
[self parseFiles:#"/untitled.py" error:&error];
if (error != nil) {
NSLog(#"I failed because: %#", error);
}
return self;
}
// Wraps with reading errors.
- (NSString *)parseFiles:(NSString *)path error:(NSError **)error {
NSStringEncoding enc1;
NSString *contents1 = [NSString stringWithContentsOfFile:path
usedEncoding:&enc1 error:*&error];
// there was a read error
// I need an asterisk here...
if (*error != nil) {
// ...and also one here
NSLog(#"FIRST ERROR: %#", *error);
return nil;
}
// here is where you'd do something that might cause another error,
// I'll just try and read a second file for simplicity
NSStringEncoding enc2;
NSString *contents2 = [NSString stringWithContentsOfFile:#"/untitled.py"
usedEncoding:&enc2 error:*&error];
// there was a SECOND error
if (*error != nil) {
NSLog(#"SECOND ERROR: %#", *error);
return nil;
}
// return both or whatever
return [NSArray arrayWithObjects:contents1, contents2, nil];
}
#end
Passing pointers around in Objective-C can get confusing. I remember having trouble grasping what needed to be done. When you have a method like this:
- (BOOL) saveValuesAndReturnError:(NSError **) error
{
BOOL success = [self doSomethingImportant];
if (!success && error)
{
// Unsuccessful and error is a valid ptr-to-ptr-to-NSError.
// Basically, someone has given us the address of a (NSError *).
// We can modify what that pointer points to here.
*error = [NSError errorWithDomain:#"myDomain" code:100 userInfo:nil];
}
return success;
}
This is intended to be invoked like this:
// If the caller doesn't care that it failed:
[someObject saveValuesAndReturnError:NULL];
// Or, if the caller wants to get error information on failure
NSError *anError = nil;
BOOL success;
// pass address of our (NSError *)
success = [someObject saveValuesAndReturnError:&anError];
if (!success)
{
// anError now points to an NSError object, despite being initialised to nil,
// because we passed the address of our NSError ptr, the method was able to
// change where `anError` points to.
NSLog (#"An error occurred while saving values: %#", anError);
}
Perhaps a very relevant read in this case is a CIMGF blog post covering exactly this topic.
However...
I remember reading a while ago that methods that return errors via method arguments such as stringWithContentsOfFile:usedEncoding:error: make no guarantee not to modify the error argument for success. In other words, you cannot rely on the value of the error parameter if the method succeeded. In your particular case, it may be better to do:
- (NSString *)parseFiles:(NSString *)path error:(NSError **)error {
NSStringEncoding enc1, enc2;
NSError *innerError;
NSString *contents1 = [NSString stringWithContentsOfFile:path
usedEncoding:&enc1
error:&innerError];
if (contents1 == nil)
{
if (error) *error = innerError;
return nil;
}
NSString *contents2 = [NSString stringWithContentsOfFile:#"/untitled.py"
usedEncoding:&enc2
error:&innerError];
if (contents2 == nil)
{
if (error) *error = innerError;
return nil;
}
// do whatever with contents1 and contents2
}