NSData Assignment Vanishes (becomes nil) Directly After Assigned - ios

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
}

Related

NSError object already populated on first method call

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).

Saving an iCloud Drive security scoped URL on iOS (UIDocumentPickerViewController)

I'm trying to save the security scoped URL returned from iCloud document picker (UIDocumentPickerViewController)
The documentation states:
If the URL is not a ubiquitous URL, save a bookmark to the file using
the
bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:
method and passing in the NSURLBookmarkCreationWithSecurityScope
option. Calling this method creates a bookmark containing a
security-scoped URL that you can use to open the file without further
user intervention.
However, the compiler says that NSURLBookmarkCreationWithSecurityScope is not supported on iOS.
Anyone know what's going on here....?
After further digging, it turns out option NSURLBookmarkCreationWithSecurityScope is NOT needed at all when creating bookmark data in IOS. It's an option for OS X. You can just pass nil for the option field. I think Apple's document is confusing at the best.
However, you do need to call startAccessingSecurityScopedResource before creating the bookmark and make sure the call returns 1 (success) before proceed. Otherwise, bookmark creation will fail. Here is the sample code:
if ([url startAccessingSecurityScopedResource]==1) {
NSError *error;
NSData *bookmark = [url bookmarkDataWithOptions:nil
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];
if (error) {
//handle error condition
} else {
// save your bookmark
}
}
[url stopAccessingSecurityScopedResource];
Again Apple's document is confusion at the best! It took me a lot of time to find out this. Hope this helps.
I ran into the same issue today, and indeed the compiler says NSURLBookmarkCreationWithSecurityScope is not available on iOS.
But to my surprise, if I use the raw constant instead (NSURLBookmarkCreationWithSecurityScope maps to ( 1 << 11 ), the method seems to work. It returns a valid bookmark data object, and when I call [[NSURL URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:stale], a valid security-scoped NSURL is returned and I can access the files and directories. Also, I tested these with iCloud Drive. And the documentation only says this should work for third-party document providers.
I am not sure how reliable this approach is, because it seems that Apple engineers didn't have time to finish up this feature, so disabled it in the last minute. Or it could be simply a bug in the header file. If anyone finds out more about this, please comment.

My NSDictionary somehow has multiple values for one key

I have been attempting to debug a issue with my code, and just came upon an odd phenomenon. Found this in the debug area when a breakpoint was triggered:
Am I correct in observing that there are multiple values for this key: #"6898173"??
What are possible causes of this? I do not set those key-value pairs using the string literal, but by getting a substring of a string retrieved and decoded from a GKSession transmission.
I still have this up in the debug area in xcode, incase theres anything else there that might help.
EDIT:
By request, here is the code that would have created one of the two strings (another was created at an earlier time):
[carForPeerID setObject:[[MultiScreenRacerCarView alloc] initWithImage:[UIImage imageNamed:#"simple-travel-car-top_view"] trackNumber:[[[NSString stringWithUTF8String:data.bytes] substringWithRange:range] intValue]] forKey:[[NSString stringWithUTF8String:[data bytes]] substringFromIndex:9]];
The string in data might look something like this:
car00.0146898173
EDIT:
Code that sends the data:
[self.currentSession sendData:[[NSString stringWithFormat:#"car%i%#%#", [(MultiScreenRacerCarView *)[carForPeerID objectForKey:peerID] trackNumber], speed, [(MultiScreenRacerCarView *)[carForPeerID objectForKey:peerID] owner]] dataUsingEncoding:NSUTF8StringEncoding] toPeers:#[(NSString *)[peersInOrder objectAtIndex:(self.myOrderNumber + 1)]] withDataMode:GKSendDataReliable error:nil];
Sorry its hard to read. Its only one line.
What you're seeing is a debugger "feechure". When you have a mutable dictionary and modify it, the debugger may not show you the correct view of the object.
To reliably display the contents of an NSMutableArray or NSMutableDictionary, switch to the console and type po carForPeerID.

why not EXC_BAD_ACCESS?

I've written the following code:
NSString *string = [[NSString alloc] initWithFormat:#"test"];
[string release];
NSLog(#"string lenght = %d", [string length]);
//Why I don't get EXC_BAD_ACCESS at this point?
I should, it should be released. The retainCount should be 0 after last release, so why is it not?
P.S.
I am using latest XCode.
Update:
NSString *string = [[NSString alloc] initWithFormat:#"test"];
NSLog(#"retainCount before = %d", [string retainCount]);// => 1
[string release];
NSLog(#"retainCount after = %d", [string retainCount]);// => 1 Why!?
In this case, the frameworks are likely returning the literal #"test" from NSString *string = [[NSString alloc] initWithFormat:#"test"];. That is, it determines the literal may be reused, and reuses it in this context. After all, the input matches the output.
However, you should not rely on these internal optimizations in your programs -- just stick with the reference counting rules and well-defined behavior.
Update
David's comment caused me to look into this. On the system I tested, NSString *string = [[NSString alloc] initWithFormat:#"test"]; returns a new object. Your program messages an object which should have been released, and is not eligible for the immortal string status.
Your program still falls into undefined territory, and happens to appear to give the correct results in some cases only as an artifact of implementation details -- or just purely coincidence. As David pointed out, adding 'stuff' between the release and the log can cause string to really be destroyed and potentially reused. If you really want to know why this all works, you could read the objc runtime sources or crawl through the runtime's assembly as it executes. Some of it may have an explanation (runtime implementation details), and some of it is purely coincidence.
Doing things to a released object is an undefined behavior. Meaning - sometimes you get away with it, sometimes it crashes, sometimes it crashes a minute later in a completely different spot, sometimes a variable ten files away gets mysteriously modified.
To catch those issues, use the NSZombie technique. Look it up. That, and some coding discipline.
This time, you got away because the freed up memory hasn't been overwritten by anything yet. The memory that string points at still contains the bytes of a string object with the right length. Some time later, something else will be there, or the memory address won't be valid anymore. And there's no telling when this happens.
Sending messages to nil objects is, however, legitimate. That's a defined behavior in Objective C, in fact - nothing happens, 0 or nil is returned.
Update:
Ok. I'm tired and didn't read your question carefully enough.
The reason you are not crashing is pure luck. At first I though that you were using initWithString: in which case all the answers (including my original one (below)) about string literals would be valid.
What I mean by "pure luck"
The reason this works is just that the object is released but your pointer still points to where it used to be and the memory is not overwritten before you read it again. So when you access the variable you read from the untouched memory which means that you get a valid object back. Doing the above is VERY dangerous and will eventually cause a crash in the future!
If you start creating more object in between the release and the log then there is a chance that one of them will use the same memory as your string had and then you would crash when trying to read the old memory.
It is even so fragile that calling log twice in a row will cause a crash.
Original answer:
String literals never get released!
Take a look at my answer for this question for a description of why this is.
This answer also has a good explanation.
One possible explanation: You're superfluously dynamically allocating a string instead of just using the constant. Probably Cocoa already knows that's just a waste of memory (if you're not creating a mutable string), so it maybe releases the allocated object and returns the constant string instead. And on a constant string, release and retain have no effect.
To prove this, it's worth comparing the returned pointer to the constant string itself:
int main()
{
NSString *s = #"Hello World!";
NSString *t = [[NSString alloc] initWithFormat:s];
if (s == t)
NSLog(#"Strings are the same");
else
NSLog(#"Not the same; another instance was allocated");
return 0;
}

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