Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding initWithBytes for an NSString

Tags:

objective-c

Assuming "someData" is an NSMutableData containing some bytes of data.

If i write the following:

NSString *someString = [NSString string];
[someString initWithBytes:[someData mutableBytes] length:[someData length] encoding:NSUTF8StringEncoding];

The second line gives me an "unrecognized selector sent to instance" error

But if I write:

NSString *someString=[[NSString alloc] initWithBytes:[someData mutableBytes] length:[someData length] encoding:NSUTF8StringEncoding];

then it works. Is there a reason the former way doesn't work? Can it be done without "alloc", (creating someString beforehand?)

Thanks.

like image 857
Dave Avatar asked Oct 17 '11 15:10

Dave


2 Answers

The reason is that the object returned by [NSString string] does not respond to the selector -initWithBytes:length:encoding:. This is because NSString's are immutable - they cannot be changed once created. The method -string takes advantage of this and just gives you a reference to a constant string (created at compile time) that is empty.

Not only that but NSString is a class cluster. This means that when you ask for an NSString, you might actually get an instance of one of its subclasses. My guess is that you are getting a subclass that has -initWithBytes:length:encoding: overridden to throw an exception because it makes no sense to send an init method to a constant string created at compile time.

In your second case, you are creating a brand new NSString at run time and then sending it an init message. This is perfectly fine. Note that, because it is a class cluster, the string returned by the init method might not be the same one as was created by the -alloc.

like image 177
JeremyP Avatar answered Oct 17 '22 09:10

JeremyP


The first way creates an empty, autoreleased string that has already been initialized and then tries to initialize the string a second time. The second way allocates memory, then initializes it appropriately. Note that the second way does not autorelease the created string, so the calling scope is still responsible. If you wanted, you could wrap the second way in a convenience method on a category of NSString to get something more similar to the first:

@interface NSString (stringWithBytes)

+ (NSString*)stringWithBytes:(const void *)bytes length:(NSUInteger)length encoding:(NSStringEncoding)encoding;

@end

@implementation NSString (stringWithBytes)

+ (NSString*)stringWithBytes:(const void *)bytes length:(NSUInteger)length encoding:(NSStringEncoding)encoding {
    NSString * aString = [[NSString alloc] initWithBytes:bytes length:length encoding:encoding];
    return [aString autorelease];
}

@end
like image 3
RPeck Avatar answered Oct 17 '22 10:10

RPeck