NSError object already populated on first method call - ios

I am following the book Test-Driven iOS development by G. Lee and came across this unit test, which I don't understand. First of all, if you need more code, please let me know right away.
-(void)testDelegateNotifiedOfErrorWhenNewsBuilderFails
{
MockNewsBuilder *builder = [MockNewsBuilder new];
builder.arrayToReturn = nil;
builder.errorToSet = underlyingError;
newsManager.newsBuilder = builder;
[newsManager receivedNewsJSON:#"Fake Json"];
...
}
-(void)receivedNewsJSON:(NSString *)objectNotation
{
NSError *error = nil;
// As you see error is nil and I am passing in a nil error.
NSArray *news = [_newsBuilder newsFromJSON:objectNotation error:&error];
...
}
#implementation MockNewsBuilder
-(NSArray *)newsFromJSON:(NSString *)objectNotation error:(NSError **)error
{
// But once I arrive here, error is no longer nil.
// (NSError **) error = 0x00007fff5cb887f0 domain: #"Fake Json" - code: 0
...
}
How is error auto-magically set?
UPDATE:
Thanks everyone for active discussion and advice. The answers explain how the caller side gets the error instance because of &, I understand that clearly. My question remains though why the callee side is pointing to a populated NSError instance, even though it had to be nil. I didn't set the error instance within newsFromJSON:error: so how is it already populated there?
I just changed [newsManager receivedNewsJSON:#"Fake Json1"]; and the error instance within newsFromJSON:error: reflects right away
(NSError **) error = 0x00007fff5b9b27f0 domain: #"Fake Json1" - code: 0. Its very confusing...

This is just pointer to pointer concept. You are passing the reference to the reference error object &error to the method -(NSArray *)newsFromJSON:(NSString *)objectNotation error:(NSError **)error;
And this will update the error object at the memory pointer you have passed.
See this is the concept of pointer to pointer.
Update:
Your error object is nil, yes its right. But you are not passing that error object to the newsFromJSON method, but the memory address of the error object( &error). That is the memory address of the error object.
This why you are getting non null value there inside your newsFromJSON method.
And one more thing, you can access the original object in your newsFromJSON method using the content of operator(* operator)
like **error = something;
This will update your original object ( NSError *error ) you declared in your caller method.
In C or CPP or Objective-C, & is the Address of operator and * is the content of operator.
&obj -> give the memory address of the obj
*obj -> give the content of the memory address in the obj.

** is a pointer to a pointer.
It means you need to pass a pointer address to a function or method.
Objective-C is a strict superset of C.
That means as in C functions and methods can only return one value.
There are two ways about it.
One is to wrap all your returns in structs or NSDictionaries or other collections.
This way is called an outParameter
It's passing a pointer address in.
C is a by copy language. But pointers are portable black holes that allow you to do wild things in C.
Objective-C and C++ give you the same wildness.
The error is set by Apple's framework code.
The Cocoa pattern is usually to return a BOOL and pass in an NSError pointer address.
If BOOL is NO check the NSError.
Apple framework will have put some presents in your NSError pointer address box.
Sometimes they don't use BOOL and instead return an object or nil.
Core Foundation C frameworks work very similarly and use in and out parameters a lot.

error is a variable of type NSError*, that is "pointer to NSError" (in objective-C, all objects are handled as references, as opposed to e.g. C++).
What this means is that error is a (local) variable that stores the address of the actual NSError object, initially nil.
The method you call creates an (autoreleased) NSError instance. In order to get a reference to that instance back, you need to pass the method the address of the pointer, or &error, which is, in turn, of type "pointer to pointer to NSError" (note the two-level indirection).
You do this because arguments to functions in C and methods in Objective-C are passed by value: if you just passed error, the value stored there (nil) alone is copied, and no matter what the called method does, the contents of the variable error on your side (the caller) can't be modified. To achieve this, you need to pass the address of error, or &error.
This way, the called method can "change" the contents of error (the address held there) so that it points to the newly created, NSError instance.
Does it make sense?
ADDENDUM: This is a very common pattern very often seen in Cocoa: The method being called could potentially fail, and instead of just using the return value to signal success/failure, and additional 'in/out' parameter is passed to retrieve detailed error information in case of failure. On failure, the method can return false (NO, 0, etc.), but in addition in can provide a more detailed error report (e.g. the reason for failure) inside the NSError instance.
EDITED: As #Droppy said, and seeing that all code involved is your own (i.e., not some first or third party framework), it is impossible that error is set to anything other than nil unless you explicitly allocate it somewhere. Perhaps you should "watch" it in the debugger to see when/where it is set. since the message seems to be set to #"Fake JSON", the first thing you could do is search that string in your project (all files).

Related

NSData Assignment Vanishes (becomes nil) Directly After Assigned

Let me start by saying I'm not proficient in objective c, nor am I an iOS developer. I'm working on a react-native app and find that I'm having to dig into the native code. So, I appreciate your patience with me and would also very much appreciate if you made zero assumptions about what I might, or might not know. Thx!
I'm trying to use react-native-mail but it fails to attach the photo I've selected to the email.
In troubleshooting, I jumped into Xcode's debugger for the first time. Stepping through the code, it appears as though the attachmentPath which is something like file:///var/mobile/... is being assigned to the variable fileData as type NSData. But then, taking one step further into the code it becomes nil.
I'm not sure why this would happen nor how to go about troubleshooting this. Here's an image of the debugger session with 3 screenshots stitched together side-by-side.
Here's the code: RNMail.m
All pointers, tips, guidance, and advice welcome
In your first screenshot, the debugger is still on the line that declares and assigns the fileData variable. This means that that line hasn't actually been executed yet. -dataWithContentsOfFile: hasn't yet been called, and thus the value that appears to be in fileData is not meaningful; what you're seeing is just garbage data prior to the variable actually being assigned. In your second screenshot, the -dataWithContentsOfFile: method has finished running, and it has returned nil. What you need to do is to figure out why you're getting nil from -dataWithContentsOfFile:. Perhaps the path to the file is incorrect, or perhaps you don't have permission to read it, or perhaps you have a sandboxing issue.
I would suggest using -dataWithContentsOfURL:options:error: instead of -dataWithContentsOfFile:. This will return an error by reference (create an NSError variable ahead of time, assign it to nil, pass a pointer to the error as the third parameter to -dataWithContentsOfURL:options:error:, and then check the error if the method returns nil). More likely than not, the contents of the error will explain what went wrong when trying to read the file.
EDIT: Looking at your screenshot again, the problem is clear; from the description of the contents of attachmentPath, we can see that it isn't a path at all, but instead it contains a URL string (with scheme file:). So you cannot pass it to the APIs that use paths. This is okay, since the URL-based mechanisms are what Apple recommends using anyway. So, just turn it into a URL by passing the string to -[NSURL URLWithString:] (or, even better, -[[NSURLComponents componentsWithString:] URL], since it conforms to a newer RFC). So, something like:
// Get the URL string, which is *not* a path
NSString *attachmentURLString = [RCTConvert NSString:options[#"attachment"][#"path"]];
// Create a URL from the string
NSURL *attachmentURL = [[NSURLComponents componentsWithString:attachmentURLString] URL];
...
// Initialize a nil NSError
NSError *error = nil;
// Pass a pointer to the error
NSData *fileData = [NSData dataWithContentsOfURL:attachmentURL options:0 error:&error];
if (fileData == nil) {
// 'error' should now contain a non-nil value.
// Use this information to handle the error somehow
}

Type casting fails in Objective C

The type casting here succeeds
id object = [NSMutableArray array];
NSURLConnection *urlCon = (NSURLConnection*)object;
if ([urlCon isKindOfClass:[NSURLConnection class]])
NSLog(#"urlCon is NSURLConnection");
else if ([urlCon isKindOfClass:[NSMutableArray class]])
NSLog(#"urlCon is NSMutableArray"); // this is called
actually since 'object' is of type NSMutableArray, the type casting for NSURLConnection should fail. but it is successful and urlCon still points to NSMutableArray, then what is the use of TypeCasting?
There are two separate issues: One is what type the variable is. The other is what type the underlying object is.
In Objective-C, casting of object pointers doesn't change what the underlying object is. And casting in Objective-C doesn't "succeed" or "fail". It's merely telling the compiler to allow you to treat the variable as if it were actually pointing to an object of the casted type. You generally do this in cases where (a) you have some knowledge of the underlying object that the compiler couldn't reasonably know, and (b) you want to be able to invoke some particular method of some class without the compiler complaining about it. Clearly, that's not the case here, where you've cast the urlCon variable to expect object to be a NSURLConnection, which it isn't.
But isKindOfClass is a runtime check, which actually looks at what type the underlying object is, and doesn't care what type the variable was cast to.
One common pattern will use isKindOfClass too determine if an object is a of a particular class, and if so, then will cast it to that particular type so that you can then use methods and/or properties of that class without warnings from the compiler.
So you'll often see this sort of pattern in processing network requests:
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // check to see if response is `NSHTTPURLResponse`
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; // if so, cast `NSURLResponse` to `NSHTTPURLResponse`
NSInteger statusCode = httpResponse.statusCode; // now you can use `statusCode` property of `NSHTTPURLResponse`
if (statusCode != 200) {
// Non 200 status code ... so handle HTTP error accordingly here
}
}
// carry on
}];
[task resume];
There are slightly simpler renditions of this pattern, but hopefully it illustrates the idea: You cast when you know (or have programmatically confirmed) something about the type of the object that is pointed to, and you just want to get the compiler on the same page so that you can now use it. You can use isKindOfClass as a runtime check to confirm that it is, indeed, that type, but the cast lets you use it as such.
What you're experiencing is entirely expected behaviour.
Casting an object of type X, to type Y, does not change the object, it merely forces the object of type X, to look like an object of type Y, even though it's of type X.
What you've done in your example, will most likely invoke lot's of undefined behaviour, if you start calling any function on the object, since NSMutableArray is not a sub- or superclass of NSURLConnection.
What you've done is a C style cast. In C, you can cast any two pointer types into each other, there are no checks involved to verify it.

Is it a good practice to extend NSError

Sorry for asking this question. I know in java we are extending Exception class for custom exceptions. But I don't see any scenarios for that in objective c.
So my question, Is it a good practice to extend NSError and introducing custom errors? If so when we should extend NSError class. I checked documentation for this too. But I can't see overriding notes for NSError.
While I agree that you shouldn't subclass NSError, it is very useful to put categories on it, and I do this regularly. For example, say your system often posts errors that come from some JSON block. I'd find it very convenient to create a category like:
#interface NSError (MyErrors)
// Construct an NSError from data in JSON.
// Include full JSON response in the userInfo
+ (NSError *)myErrorWithJSON:(JSON *)json;
// Parse the text out of the user info
- (NSString *)myErrorMessage;
// The full JSON error block as a string
- (NSString *)myErrorJSON;
// BOOLs can be helpful sometimes, or you could return an enum for use in a switch.
- (BOOL)myIsNetworkError;
- (BOOL)myIsAuthError;
#end
I often write little helpers to construct NSError more simply, construct the userinfo the way I want, and the pull data back out of the userinfo without callers needing to know its internal representation. I find this to be a very good form of data-hiding, and encourages the use of more descriptive messages.
Similarly, even for smaller projects, I often create a +myErrorWithCode:localizedDescription: category method. I know my domain, so I usually don't need to pass that, and this makes it a lot easier to set the NSLocalizedDescription key in the user info. Again, this encourages better errors by making them easier to create, and makes it easier to change the implementation details of your error handling.
I've never seen it done and that's because NSError is already very versatile. It allows the type of error to be defined by setting the domain and code properties and allows arbitrary additional information to be attached within the userInfo dictionary.
So, no, it's not good practice.
In the documentation is written that it is ok to subclass:
Applications may choose to create subclasses of NSError, for example,
to provide better localized error strings by overriding
localizedDescription.
In my case I am working with OSStatus which is Int32. NSError constructor supports only Int. So I need to subclass it to support OSSStatus.
It's not a bad idea to extend NSError.
I also have made a category on NSError for my own use. I would like to share it with you.
(1) Make a strings file to define all the error codes:
/* Following are general error messgaes those we can show to user
regarding to Internet connection and request. You can add more
codes. */
"-1001" = "Connection time out";
"-1003" = "Cannot find Host";
"-1004" = "Cannot connect to Host";
"-1005" = "Server is temporarily down";
"-1009" = "The Internet connection appears to be offline";
"-1012" = "Authentication failed";
"2000" = "This is a custom error message"; // custom created error code
/* Always describe unknow error with whatever you want in
except case (i.e. except error codes). If not mentioned
the following line, still you will get message "Unknown error" */
"Unknown error" = "Network error occured";
(2) Make a category on NSError, let say "NSError+ErrorInfo":
#interface NSError (ErrorInfo)
-(NSString *)userDescription;
#end
(3) Define it:
#define ERROR_KEY(code) [NSString stringWithFormat:#"%d",code]
#define ERROR_LOCALIZED_DESCRIPTION(code) NSLocalizedStringFromTable(ERROR_KEY(code),#"Errors",nil)
#implementation NSError (ErrorInfo)
-(NSString *)userDescription
{
NSString *errorDescrption = NSLocalizedStringFromTable(ERROR_KEY(self.code),#"Errors",nil);
if (!errorDescrption || [errorDescrption isEqual:ERROR_KEY(self.code)]){
return NSLocalizedStringFromTable(#"Unknown error",#"Errors",nil);;
}
else{
return ERROR_LOCALIZED_DESCRIPTION(self.code);
}
return nil;
}
#end
(4) Make use of it:
NSError *yourError; // This can be any NSError object you get
yourError = [NSError errorWithDomain:#"yourDomain" code:2000 userInfo:details]; // Just for test
NSLog(#"%#",[yourError userDescription]);

What is Apple warning against in ARC documentation caveat about pass by reference?

In Apple's documentation about ARC, they make a point of spelling out a problematic scenario in which ARC will generate a boilerplate temporary variable behind the scenes. Search on "The compiler therefore rewrites":
https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
The gist of the warning seems to be that because the stack-based variable is "strong" and the by-reference parameter to the called method (performOperationWithError:) is autoreleasing, ARC will generate a temporary local variable to serve the memory management needs of the autoreleasing variable. But because the temporary variable is assigned to the strong variable in the boilerplate example, it seems as though from a client's point of view there is no risk.
What exactly is the documentation taking pains to warn us about here? What is the risk as either a client or as an implementor of a method that may be called in this way (with an autoreleased, return-by-value parameter)?
It's only a warning about less than ideal performance. In the rewritten code, the NSError pointed to by "tmp" comes back autoreleased, is retained when assigned to "error", and then is released again when "error" goes out of scope.
If you change the declaration in the original code to:
NSError __autoreleasing *error;
If you do this, there is no assignment to a temp, and that implicit retain and then release no longer occurs. (The NSError object itself is still valid for exactly as long as it was before, since it is still in the autorelease pool.) So the documentation is warning you that if you use the "wrong" variable qualifier that it can cause extra retain count munging that wouldn't otherwise be required.
Also note that with either version of the code: Because the variable in question is passed by reference and isn't the return value from -performOperationWithError:, there isn't the opportunity to do the magic stack walking trick that ARC can do to save the object from going into the autorelease pool in the first place.
I think it’s to prevent confusion if you start looking at the values passed into the method. In their example, if I set a breakpoint on the line that calls [myObject performOperationWithError:&tmp]; and type p error, I’ll see the address of it. But if I step into -performOperationWithError: and type p error, I’ll get a different value—inside the method, error points to that temporary value.
I can see a situation where some poor sap is trying to debug something tricky with ARC where the pointer changing as it gets passed into the method would be an extremely confusing red herring.
My guess: If you made assumptions about the memory referenced by the output parameter, e.g indexing off the pointer, you might be surprised.
I don't think it has anything to do with the client. It looks like a reference to the same issue addressed in the WWDC 2013 video on memory issues: If you yourself implement a method that takes an autoreleasing indirection parameter (such as an NSError**), and if you create an autorelease pool block inside that method, do not assign to the NSError from inside the autorelease pool block. Instead, assign to a local variable, and then assign from the local to the NSError outside the autorelease pool block.
Seems to me that it's less of a warning about this behavior than a description of what the compiler does in this case and why you can pass the address of a strong local error reference to a method that is declared as wanting an __autoreleasing reference and not trigger a complaint.
You generally want an API to use __autoreleasing on such a parameter in case it is being used by either ARC or non-ARC code, as in non-ARC code it would be unusual to have to release such an output parameter.
The Apple documentation is referring to a compiler misfeature that will synthesize a temporary variable for you to deal with the conversion between __block and __autoreleasing. Sadly, this doesn't solve very many problems and it produces potentially disasterous unexpected results.
For example:
int main(int argc, char *argv[])
{
__block id value = #"initial value";
void (^block)(id *outValue) = ^(id *outValue){
value = #"hello";
};
block(&value);
NSLog(#"value = %#", value);
return 0;
}
With ARC, this reports:
2013-04-24 13:55:35.814 block-local-address[28013:707] value = initial value
but with MRR:
2013-04-24 13:57:26.058 block-local-address[28046:707] value = hello
This very often comes up when using NSFileCoordinator, causing you to lose the resulting NSError!
#import <Foundation/Foundation.h>
int main(int argc, char *argv[])
{
NSURL *fileURL = [NSURL fileURLWithPath:#"/tmp/foo"];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
__block NSError *error;
[coordinator coordinateWritingItemAtURL:fileURL options:0 error:&error byAccessor:^(NSURL *newURL){
NSDictionary *userInfo = #{
NSLocalizedDescriptionKey : #"Testing bubbling an error out from a file coordination block."
};
error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOSYS userInfo:userInfo];
}];
NSLog(#"error = %#", error);
}
When compiled with ARC, this results in a nil error!
This has been written up as a bug at llvm.org for a while, though I just changed the title to make it more clear that I'm suggesting the feature be ripped out. Also attached to that bug is a patch to add a new flag -fno-objc-arc-writeback to disable the feature).

Using output parameters with ARC

So I have read this question, which seems to be exactly the kind of problem I am having, but the answer in that post does not solve my problem. I am attempting to write a data serialization subclass of NSMutableData. The problematic function header looks like this:
-(void)readString:(__autoreleasing NSString **)str
I do some data manipulation in the function to get the particular bytes the correspond to the next string in the data stream, and then I call this line:
*str = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
No errors in this code. But when I try to call the function like so:
+(id) deserialize:(SerializableData *)data
{
Program *newProgram = [[Program alloc] init];
[data readString:&(newProgram->programName)];
On the line where I actually call the function, I get the following error:
Passing address of non-local object to __autoreleasing parameter for write-back
I have tried placing the __autoreleasing in front of the NSString declaration, in front of the first *, and between the two *'s, but all configurations generate the error.
Did I just miss something when reading the other question, or has something in the ARC compiler changed since the time of that post?
EDIT:
It seems that the problem is coming from the way I am trying to access the string. I can work around it by doing something like this:
NSString* temp;
[data readString&(temp)];
newProgram.programName = temp;
but I would rather have direct access to the ivar
You can't. You might gain insight from LLVM's document Automatic Reference Counting, specifically section 4.3.4. "Passing to an out parameter by writeback". However, there really isn't that much extra detail other than you can't do that (specifically, this isn't listed in the "legal forms"), which you've already figured out. Though maybe you'll find the rationale interesting.

Resources