Wingzero Wingzero - 28 days ago 9
Objective-C Question

why [NSData bytes] can be changed and impact NSData object itself

Say I have below code from another guy:

-(NSString *)xorBetweenString1:(NSString *)str1 andString2:(NSString *)str2 {
NSData *d1 = [str1 dataUsingEncoding:4];
const void *b1 = [d1 bytes];

NSData *d2 = [str2 dataUsingEncoding:4];
const void *b2 = [d2 bytes];
const void *b3 = b2;

int c = 0;
for (int i = 0; i < [d1 length]; i ++) {
*(Byte *)b1++ ^= *(Byte *)b3++;
c++;
if (c == [str2 length]) {
c = 0;
b3 = b2;
}
}

NSString *result = [[NSString alloc] initWithData:d1 encoding:4];
return result;
}


As I remember, [NSData bytes] returns
const void *
, which means the content the pointer points is not mutable. However, above function indeed changes NSData's contents:

before:


(lldb) po v20
<32383a63 663a6461 3a62613a 64313a39 33>


after:


(lldb) po v20
<166c7a31 327a3431 1e362168 30716a69 17>


I am confused, why no errors? Or I made any mistake?

UPDATE:
I found [str1 dataUsingEncoding:4] is returning
NSConcreteMutableData
. Seems the root cause. But why it returns
NSConcreteMutableData
instead pure
NSData
? I mean the apple doc never mentioned it?

Rob Rob
Answer

The above code is allowed, not because there's a NSConcreteMutableData behind the scene, but rather simply because the author is casting a const pointer to a non-const pointer. In Objective-C, you can bypass all type and mutability safety when you engage in casts.

This mutating of data pointed to by the bytes reference is a horrible practice because you have no assurances as to what assumptions or actions this NSData may have made with respect to this underlying data buffer. You should be doing a mutableCopy to get NSMutableData and then working with mutableBytes or using one of the replaceBytes... methods.

As the mutableBytes documentation says:

This [mutableBytes] property is similar to, but different than the bytes property. The bytes property contains a pointer to a constant. You can use the bytes pointer to read the data managed by the data object, but you cannot modify that data. However, if the mutableBytes property contains a non-null pointer, this pointer points to mutable data. You can use the mutableBytes pointer to modify the data managed by the data object.

Comments