NSData's dataWithBytesNoCopy and writeToFile - iphone

I have a category method that does somethign like this:
#implementation NSData (additions)
- (id)someFunc {
char *buffer = malloc(255);
NSUInteger length = 0;
while (true) {
// . . . fill buffer[length++];
}
realloc(buffer, length);
return [NSData dataWithBytesNoCopy:buffer length:length freeWhenDone:NO];
}
I am then trying to write the returned data (well call this NSData *fileData):
NSError *error;
NSData fileData = [NSData someFunc];
[fileData writeToFile:somePath options:NSDataWritingAtomic error:&error];
I get an error:
Error Domain=NSCocoaErrorDomain
Code=512 "The operation couldn’t be
completed. (Cocoa error 512.)"
UserInfo=0x20deffd0
{NSFilePath=/Users/user/Library/Application
Support/iPhone
Simulator/4.3/Applications/4C315580-153D-4FA7-9474-E17B58832515/Library/Caches/file.pdf,
NSUnderlyingError=0x20de1fe0 "The
operation couldn’t be completed. Bad
address"}
The path exists and is valid. I think the issue is the returned NSData is just a light wrapper around an array allocated with malloc and that writeToFile does not know how to handle this. Does anyone see what I am doing wrong?

When you call realloc you must save the returned pointer, since realloc may allocate a new buffer and copy the content of the old location to that new location. Afterwards the old location is freed and thus invalid. A different malloc call may overwrite that old location.
So you need to do:
buffer = realloc(buffer, length);

That code doesn't make any sense.
show the code that you use to fill the buffer
you have a malloc(), why do you realloc() after the loop? That is a waste of cycles and, if the goal were to expand the buffer if the input data were too long, it won't work as written.
you must check the return value from writeToFile:options:error: to know whether or not there is an error; you cannot check the error directly.
also, don't stick a category like that onto an existing class. In general, it is indicative of an architecture that is fragile and poorly layered. If you do go this route, at least prefix your method with something unique.
It isn't clear why that particular error came up. That path seems a little wonky maybe, how are you generating it?

Related

writeToFile how to tell when it is completed

I'm doing some writing of data to my documents directory and I was wondering if there is a way to tell when my writeToFile method completes and the file I am creating has be fully created. Thanks in advance here is the method I am calling I am just looking for a way wether it is a delegate method or some other way to tell when this completes.
[imageData writeToFile:fullPathToFile atomically:YES];
Nick
The method writeToFile:atomically is "synchronous". It will write the file and then return YES or NO depending on wether the file was successfully written or not.
That means that the operation is complete as soon as the method returns.
BOOL success = [imageData writeToFile:fullPathToFile atomically:YES];
// Now, the operation is complete
// success indicates whether the file was successfully written or not
Swift 5:
do {
try data.write(to: path)
// here's when write operation is completed without errors thrown
} catch {
Swift.print(error)
}

Getting length of NSMutableData

I have managed to NSInputStream and read some data to NSMutableData object. I am able to put this data into string and NSLog it, however when I try to access its length(I am assuming this is its size in bytes) my app crashes.
NSString *stringData=[[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding];
NSLog(#"%# thats data",stringData);//logs out content of data
NSLog(#"%# thats data length",[self.data length]);//crashes
So my question is if I call copy on NSMutableDate do I get immutable copy ?
Am I tying to access the length in a wrong manner ?
It's because you are trying to log the length as an object using %#. It's not an object, it's an integer, so log it with %i instead:
NSLog(#"%i thats data length",[self.data length]);
Logging an object with %# tries to call the [... description] method on whatever is passed in. You can imagine the horrors that occur in the application memory when it tries to call that method on a random integer, thinking that it's a pointer to an object.

Object is deallocated - why? where?

Ok, I spent the last 8 hours fighting with it - it just seems beyond me. Here's my complete (relevant) code:
- (void)updateUserDefaults
{
NSMutableDictionary *viewControllerDetails = [[NSMutableDictionary alloc] initWithCapacity:4];
[viewControllerDetails setObject:[NSNumber numberWithInt:OOVenueClassControllerType] forKey:#"classType"];
[viewControllerDetails setObject:self.searchTerm forKey:#"searchTerm"];
[viewControllerDetails setObject:self.searchLocation forKey:#"searchLocation"];
//----- the next two lines cause the problem
NSString *res = [[NSString stringWithFormat:#"%#",[searchResults xmlString]] retain];
[viewControllerDetails setObject:res forKey:#"searchresults"];
//-----
NSMutableArray *viewControllersList = [NSMutableArray array] ;
[viewControllersList addObject:viewControllerDetails];
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
//the following line causes the error
[defs setObject:viewControllersList forKey:kViewControllersKey];
[defs synchronize];
[res release];
}
Note the block with the next two lines cause the problem. At first I didn't create another string, but added it later while trying to solve the problem.
If I comment out those two lines, everything works fine. If I put them back in, I get
- [CFString class]: message sent to deallocated instance 0xa1a9000
Is something is wrong with the string that I'm trying to put into the userdefaults? That string is rather large (about 200,000 characters), but I had stored even longer strings in user defaults in the past.
It's also worth noting that if I uninstall the app, then everything works fine. But on subsequent runs the problem exhibits itself.
So, how and why and where is the string getting deallocated? I have explicitly added retain - but that still doesn't help. What am I missing?
Edit: just realised I forgot to say that the error is thrown on line
[defs setObject:viewControllersList forKey:kViewControllersKey];
Also, for general information, method - (NSString *)xmlString on searchResults does exactly what the name means: creates an XML string with the information from that object.
Edit 2: I tried doing something else with that string - convert it to NSData, compress using zlib - but regardless of data type, that particular object gets deallocated. Does it have to do something with the size of the string?
NSString *res = [[NSString stringWithFormat:#"%#",[searchResults xmlString]] retain];
Is auto released. You don't need to release it at the end of your method. You are over-releasing it.
Further, you don't need to retain the [searchResults xmlString]. The stringWithFormat method already does it for you.
Good Luck!
Ok, not sure what exactly the problem was, but it was somewhere in the searchResults and/or xmlString method. searchResults object is originally created from XML received from the server (XML is parsed into the object structure). When xmlString was called, for some reason the string I was getting back was different from the original XML (I'm not talking about formatting, of course) - of 200,000 char-long string, within the first 500 chars or so there were some differences. I haven't been able to figure out why. So, instead of recreating the xml from object structure, I instead stored the original XML in a field in that object and, when xmlString was called, simply returned the original string. Now everything worked fine.
Thank you all for your support through this painful process.

iPhone: Reading a string from a file is returning the wrong results

What I'm doing:
I am reading some data off a file several times while my app runs. I use the following code to do so:
NSString *dataPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"data.txt"];
NSString *data = [NSString stringWithContentsOfFile:dataPath encoding:NSStringEncodingConversionExternalRepresentation error:NULL];
NSArray *components = [data componentsSeparatedByString:#"|||||"];
The first time this is called, it works as expected - I get an array of length 5, each section containing a relevant string.
What goes wrong:
This file is never modified. Yet, when I call the same procedure a second time (or third, fourth etc) I don't get the same result. Instead, an array of length 1 is returned, containing only part of the necessary data.
Why? I can't see any reason for this to go wrong... any help is much appreciated!
Since the file is in you AppBundle this means that you can't modify this file at all.
Are you sure that, where ever this code is called, the autorelease object are retained correctly?
If you call this block of code every time you want this data, it might be an idea to save the results of the first time and use that every time. This will speed things up a bit.
Turns out that the code works fine. The problem was elsewhere in my code, where I was (accidentally) accessing protected directories. As a result, iOS blocked my app from accessing any files at all :o

NSURL into NSData (Cocoa error 256.)

I need to serialize my NSURL.
object is type of NSManagedObject.
NSURL *objectURIRepresentation = [[object objectID] URIRepresentation];
NSError *error = nil;
NSData *objectIDData = [NSData dataWithContentsOfURL:objectURIRepresentation options:NSDataReadingMapped error:&error];
I get error: (Cocoa error 256.).
Any ideas? Something tells me, using dataWithContentsOfURL: is not good idea.
Update
One more question which is put as a comment mistakenly:
What is the difference between [NSData dataWithContentsOfURL:uri]; and [NSKeyedArchiver archivedDataWithRootObject:uri];?
Thanks.
Firstly, your code does not attempt to serialize a NSURL object, it attempts to create a data object out of the data at the URL returned as the URI of a managed object.
Secondly, that is never going to work.
[NSData dataWithContentsOfURL:] will try to read a file at a particular URL. The URI of a managed object represents an object stored in pieces with many others inside a persistent file like a SQLite database.
The URI only allows a managed object context to identify a particular object in its own store. The URI is gibberish to anything else other than the context.
NSManagedObject does not implement the NSCoder protocol so managed objects cannot be serialized. I'm not sure what you want to do here but you can't do it this way.
As mentioned here Cocoa error 256 core data
error code 256 can occur when an unknown error is occurred in reading the resource or the path has some encoded characters in it.
What it seems to me is you are trying to get the data from NSManagedObject. Hence as #fluchtpunkt suggested you should look for http://cocoawithlove.com/2008/08/safely-fetching-nsmanagedobject-by-uri.html
Now coming to your second question [NSData dataWithContentsOfURL:uri]; returns data for a web url or a local resource in your documents directory. While [NSKeyedArchiver archivedDataWithRootObject:uri]; returns the NSData object containing the encoded form of the object graph whose root object is given.
try to add "Http://" before in the link