Wingzero
Wingzero

Reputation: 9754

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?

Upvotes: 1

Views: 633

Answers (1)

Rob
Rob

Reputation: 438152

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.

Upvotes: 3

Related Questions