• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: NSData weirdness...
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSData weirdness...


  • Subject: Re: NSData weirdness...
  • From: Bill Bumgarner <email@hidden>
  • Date: Thu, 20 Jun 2002 23:52:40 -0400

On Thursday, June 20, 2002, at 11:24 PM, email@hidden wrote:
Dear All,

I have written an experiment class to play around with Cocoa and
NSBundles and so on, and I have a mind numbing question about NSData
usage.

Always a good plan... play with the APIs in a sandbox that minimizes variables before trying to use them in a real context!


Below is a Level.m file listing that is set to be my NSApp delegate (set
in Interface Builder).

// START FILE CONTENTS

... code deleted ....

// END OF FILE CONTENTS

The problem is this: the subdataWithRange: message does not seem to
work. You see the LevelData.data file in the app bundle is approx 60K in
length. But when I view the output from the NSLog() line, instead of
just the 0..100 byte range I get the entire file contents. Arghh!

First, instead of...

[[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/LevelData.data"]

... use ...

[[NSBundle mainBundle] pathForResource: @"LevelData" ofType: @"data"]

... it is both more portable (both across platforms-- if that ever happens again) and across versions of the OS/Foundation/Wrappers.

Now, the data problem...

-bytes returns the base address of the memory that contains the data contained within the NSData object. Because the original NSData object is immutable and -subdataWithRange: returns an immutable NSData instance, the two NSData instances contain a pointer to the exact same hunk of memory. It can't change, so why copy it?

The NSLog(@"%s", [data bytes]) is attempting to treat the contents of the NSData object as a C-String; that is, as a NULL terminated string that starts at the base address of the data contained within the NSData instance.

As such, if there doesn't happen to be a NULL byte within the first 100 bytes of the data contained in the NSData object produced by subdataWithRange: (with a range of 0,100), NSLog() will happily keep printing characters well beyond the range of memory that is contained within the NSData object.


Another problem was that I had mysterious info.plist type stuff appended
after the contents NSData* variable when I was using the [NSData
dataWithContentsOfFile:] message. The NSLog() method would spit out the
entire file contents (instead of the subrange) and append mumbo jumbo
like:

.... stuff deleted ...

NSData is a class cluster. That is, when you call one of the factory methods-- like +dataWithContentsOfFile: or +dataWithContentsOfMappedFile:-- it is very likely that you are actually receiving instances of two different subclasses of NSData. One is optimized for non-mapped files, the other for mapped files. -subdataWithRange: may likely return yet a third subclass.

That it is returning a subclass is generally irrelevant to your code; it is an NSData and treat it as such, all is well.

However, when abusing the APIs-- in this case, when trying to dump the contents of the NSData using NSLog() on a hunk of memory that doesn't happen to contain a NULL character-- the behavior will be undefined.

It is likely that +dataWithContentsOfFile: works in a fashion where -subdataWithRange: makes the assumption that it really does need to copy the data. So, when the NSData object returned by -subdatawithRange: was created, it copied exactly the first 100 bytes.

Now, the first 100 bytes doesn't actually have a NULL character in it. So,
when you call NSLog() to print the contents of the data, NSLog() runs off the end of the data and continues to dump whatever happens to be in memory.
In this case, there was obviously an NSString (or char *) buffer that contained the XML representation of a property list. When you called NSLog, it ran off the end of your data and started spewing the property list.

In otherwords -- a buffer overrun.

Finally -- there is no magic. Think of what your code would look like if it were "standard C".

I.e. -- total pseudo code here... haven't written this kind of stuff in years:

unsigned char buf[100];
FILE *dataFile = fopen("LevelOne.data", "r");
fread(buf, dataFile, 100);

In the above, what would happen if you did....

fprintf(stdout, "%s", buf)

... when the first 100 bytes of LevelOne.data didn't actually contain a NULL/'\0' character?

Same thing as the ObjC code...

b.bum
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.

  • Follow-Ups:
    • NSTree ??
      • From: email@hidden
  • Prev by Date: Re: NSData weirdness...
  • Next by Date: Re: Outputting an unsigned long long
  • Previous by thread: Re: NSData weirdness...
  • Next by thread: NSTree ??
  • Index(es):
    • Date
    • Thread