What is the difference between these two objective-c statements?
NSData *documentBytes = [NSData dataWithContentsOfFile:filePath];
versus this:
NSData *documentBytes = [NSData initWithContentsOfFile:filePath];
From Apple's NSData Class Reference page, it states the following about each
dataWithContentsOfFile - Creates and returns a data object by reading every byte from the file specified by a given path.
initWithContentsOfFile - Returns a data object initialized by reading into it the data from the file specified by a given path.
To me, these seem functionally equivalent but I highly doubt they do the same thing in all cases, right?
Thanks in advance...
-Ergin
When you use init, you always have to use alloc, like so:
NSData *documentBytes = [[NSData alloc] initWithContentsOfFile:filePath];
This returns an NSData object with a retain count of 1, you now own the reference and are responsible for releasing it.
When using dataWithContentsOfFile
NSData *documentBytes = [NSData dataWithContentsOfFile:filePath];
You get back an autoreleased NSData object. You can use it and forget about it, the autorelease pool will take care of it. If you want to store it, you have to retain it.
Of course, when you are using ARC, you can forget about all of this ;-), the methods are essentially the same.
The second
NSData *documentBytes = [NSData initWithContentsOfFile:filePath];
Will not compile you will need to alloc it first, will look something like:
NSData *documentBytes = [[NSData alloc] initWithContentsOfFile:filePath];
But one or another will work the same, in the end you will have an NSData that has the contet of the file. The first one is a shortcut.
So about your doubt:
The first one you do not need to alloc the object first, the method will return the object for you, if you are not using ARC (I do not think so), the first one will return an object that the system will take care.
The second one you will need to alloc the object first, the method only initialize your object, and if you are not using ARC you will need to take care to release it.
Related
Is it a problem for me to do the following to change a mutable data instance immutable?
NSMutableData *mutData = [[NSMutableData alloc] init];
//Giving some value to mutData
NSData *immutableData = [NSData dataWithData:mutData];
[mutData release];
This is completely okay, and is in fact one of the primary uses of dataWithData: -- to create an immutable copy of a mutable object.*
NSData also conforms to the NSCopying protocol,** which means you could instead use [mutData copy]. The difference is that dataWithData: returns an object you do not own (it is autoreleased), whereas per memory management rules, copy creates an object for whose memory you are responsible. dataWithData: is equivalent in effect to [[mutData copy] autorelease].
So you can choose either dataWithData: or copy, dependent upon your requirements for the lifetime of the resulting object.
*This also applies to similar methods in other classes which have a mutable subclass, e.g., +[NSArray arrayWithArray:].
**See also "Object Copying" in the Core Competencies Guide.
No it is not a problem. immutableData will be initialized with the data in mutData.
I'm working on writing to a file one user input on a textField.
So far I have one NSFileManager which writes data to a file. Still, I have no way of putting the textField input inside a file.
Is there a way do add a string value to NSData so I can write it?
you can get NSData from NSString,
NSData *newData = [yourString dataUsingEncoding:NSUTF16StringEncoding];
use encoding that fits your case.
append the obtained data to existing NSData,
[existingData appendData:newData]
Note: "existingData" should be an instance of NSMutableData.
Use below code as your reference.
NSString* myString = #"Is there a method for adding one NSString for NSData";
NSData* myData= [myString dataUsingEncoding:NSUTF8StringEncoding];
I have a comprehension question. This method is given:
- (NSArray*)test {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://stackoverflow.com/"]];
NSString *result = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSMacOSRomanStringEncoding];
result = [result stringByAppendingString:#"something"];
NSArray *arr = [NSArray arrayWithObject:result];
//[result release];
return arr;
}
If I uncomment the release the App would crash and say it cannot access a released object.
By not releaseing the result string Instruments would report a leak (NSPlaceholderString).
I can autorelease it on the same line I alloc it, that would solve the problem (which I'm currently doing in my App).
If I understand it correctly stringByAppendingString: should create an autoreleased object so the 'old' result could be deallocated. Then the method arrayWithObject: should copy the object into an array. So my thought was to release the string after it was copied to the array.
Am I missing something or something wrong with my knowledge?
Let's go through your code line by line.
- (NSArray*)test {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://stackoverflow.com/"]];
This creates a data object. You don't own it, but it will stick around for the rest of the method's time. So far, so good.
NSString *result = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSMacOSRomanStringEncoding];
This creates a string object that you own. Again, no problem here — we just need to release it later.
result = [result stringByAppendingString:#"something"];
You throw away your reference to the string object that was in result and store a new string object that you do not own. This is a leak because you can no longer release the original string. Also, you're correct in noting that the new string can be treated as an autoreleased object — which means you should not release it.
NSArray *arr = [NSArray arrayWithObject:result];
Contrary to your belief, this does not copy anything. It merely keeps a reference to the new string and retains it.
//[result release];
You should not release result at this point, because the object it contains is not one you own — you got it from stringByAppendingString:, not from a method with new, alloc, retain or copy in its name. Releasing this object that you do not own will almost certainly result in a crash at some point. The old object that you own and should release was lost two lines earlier, and releasing something else in its place won't help.
result = [result stringByAppendingString:#"something"];
This line replaces the first allocated string by a new autoreleased string.
So the first string is leaked and the second one should not be released. This explains why uncommenting the release line crashes.
I have the following code for creating an NSString to contain the body of the text file and then convert it to NSData and output it to a file.
NSString *particleString = [[NSString alloc] initWithFormat:#"%#", #"This is the body of my file"];
NSData *metaVals = [particleString dataUsingEncoding:NSISOLatin1StringEncoding];
Since I have created particleString using alloc, I thought I needed to release it after I have finished converting it to NSData, hence I added
[particleString release];
But my app crashes when I add this line. However, when I remove the line where I use it to create metaVals, I can safely release it.
Can anyone explain why passing it to NSData stops me from releasing it? I believe I own particleString, what's going on?
According to your comment you do particleString = [particleString stringByAppendingFormat:#"%#", #"Some other string"]; which looses the reference to the original particleString, and replaces it with an autoreleased version. You then go on to release the autoreleased version, causing both a leak of the original particleString, and an overrelease of the new one.
Try this
NSString *particleString = [NSString stringWithFormat:#"%#", #"This is the body of my file"];
particleString = [particleString stringByAppendingFormat:#"%#", #"Some other string"];
NSData *metaVals = [particleString dataUsingEncoding:NSISOLatin1StringEncoding];
It no longer has a release because both strings are now autoreleased.
I would suggest re-reading the memory management rules
[particleString stringByAppendingFormat:#"%#", #"Some other string"];
this returns a newly created object. which means the first object created and assigned to particleString is leaking, and you are trying to release the newly, autoreleased, object that has been created by using the -stringByAppendingFormat: method
You could probably use a NSMutableString instead which has a method named
appendFormat:(NSString *)...
I have malloc'd a whole mess of data in an NSOperation instance. I have a pointer:
data = malloc(humungous_amounts_of_god_knows_what);
uint8_t* data;
How do I package this up as an NSData instance and return it to the main thread? I am assuming that after conversion to an NSData instance I can simply call:
free(data);
Yes?
Also, back on the main thread how do I retrieve the pointer?
Thanks,
Doug
You want one of the -dataWithBytes:length: or its variants:
NSData *d = [NSData dataWithBytes:data length:lengthOfDataInBytes];
which copies the bytes and you can then free(data). To save a copy, assuming data is allocated using malloc use:
NSData *d = [NSData dataWithBytesNoCopy:data length:lengthOfDataInBytes];
You should not call free on your buffer in this case, as the NSData instance will free it for you.
Note that all of these methods return an autoreleased instance, so you will probably have to retain it if you want to keep it around between threads (and aren't using GC). You can use the equivalent alloc/initWithBytes:... initializers instead.
To get a pointer to an NSData's contents, use bytes.
(I think a few minutes with the NSData documentation will serve you well)
Thanks Barry,
I actually went with something even simpler.
Put an NSValue wrapper around the pointer:
NSValue *value = [NSValue valueWithPointer:imageData];
Stuff it in a dictionary to be returned to the main thread:
[result setObject:value forKey:cubicFaceName];
Back on the main thread, when I'm done with the data I discard it:
uint8_t *bits = [value pointerValue];
free(bits);
Cheers,
Doug