27

I have an NSData object of approximately 1000kB in size. Now I want to transfer this via Bluetooth. It would be better if I have, let's say, 10 objects of 100kB. It comes to mind that I should use the -subdataWithRange: method of NSData.

I haven't really worked with NSRange. Well, I know how it works, but I can't figure out how to read from a given location with the length: 'to end of file'... I've no idea how to do that.

Some code on how to split this into multiple 100kB NSData objects would really help me out here. (it probably involves the -length method to see how many objects should be made..?)

Thank you in advance.

  • Also regarding Bluetooth data size, from http://developer.apple.com/library/ios/#DOCUMENTATION/NetworkingInternet/Conceptual/GameKit_Guide/GameKitConcepts/GameKitConcepts.html "For best performance, it is recommended that the size of the data objects be kept small (under 1000 bytes in length)." – user149100 Mar 16 '12 at 10:15

1 Answers1

38

The following piece of code does the fragmentation without copying the data:

NSData* myBlob;
NSUInteger length = [myBlob length];
NSUInteger chunkSize = 100 * 1024;
NSUInteger offset = 0;
do {
    NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
    NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[myBlob bytes] + offset
                                         length:thisChunkSize
                                   freeWhenDone:NO];
    offset += thisChunkSize;
    // do something with chunk
} while (offset < length);

Sidenote: I should add that the chunk objects cannot safely be used after myBlob has been released (or otherwise modified). chunk fragments point into memory owned by myBlob, so don't retain them unless you retain myBlob.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • I'm not completely getting that. `NSData *chunk = [NSData dataWithBytesNoCopy:[myBlob bytes] + offset length:thisChunkSize freeWhenDone:NO];` is throwing a warning of course. I don't think you can say `[myBlob bytes] + offset`. Offset has nothing to do with bytes. I don't seem to understand your logic here. – Cedric Vandendriessche May 24 '10 at 18:12
  • I edited the code to remove the compiler warning. The rest of the code seems right to me, though I wrote it without testing. `offset` is the amount of bytes already processed. That's why every new chunk is initialized at the beginning of the data array plus the `offset`. – Nikolai Ruhe May 24 '10 at 19:17
  • You're saying that the `chunk` objects point into memory owned by `myBlob`. Does this mean when I transfer chunks over bluetooth they'll be corrupted on the other side? Or do you mean that I can send them but they won't be available anymore on the sender's side after releasing `myBlob`? – Cedric Vandendriessche May 29 '10 at 11:16
  • It's all about your process. In this process, chunk objects would point to abandoned memory if `myBlob` would be released before the chunk object. On the receiver side, the address space and memory management of your process has no relevance. – Nikolai Ruhe May 29 '10 at 12:01
  • Thanks for your help, all is working fine. If I use `NSData *chunk = [NSData dataWithBytes:(void*)[myBlob bytes] + offset length:thisChunkSize];` I can release the blob. May I ask why you chose the `-dataWithBytesNoCopy:`? – Cedric Vandendriessche May 29 '10 at 15:36
  • @NikolaiRuhe: `NSData* chunk = [NSData dataWithBytesNoCopy:(void*)[myBlob bytes] + offset length:thisChunkSize freeWhenDone:NO];` gives a error saying "Arithmetic to point on a void". What can be done in that case? – Parth Bhatt May 24 '12 at 10:27
  • @ParthBhatt Looks like the compiler adopted the C++ idea that pointer arithmetic on a void pointer is invalid. Use a char pointer instead. I edited the answer to reflect this. – Nikolai Ruhe May 25 '12 at 07:34
  • 10
    Thanks! Also we can use NSData *chunk = [myBlob subdataWithRange:NSMakeRange(offset, chunkSize)]; – Rubycon Sep 25 '12 at 13:18
  • @NikolaiRuhe: You Are Awesome .I wanted to parse 10MB of document and .. WOW ... Earlier it was giving me MemWarning on Device but now, it is as smooth as butter . Thanks Buddy – Abhishek Bedi Jan 25 '13 at 08:55
  • What does the (char *) do? – GTG101 Apr 19 '17 at 19:18
  • @GTG101 It casts the void pointer returned from `[myBlob bytes]` to a byte sized pointer (to enable pointer arithmetic). – Nikolai Ruhe Apr 19 '17 at 20:30
  • I changed it to swift like this: – GTG101 Apr 19 '17 at 21:46
  • var chunk = Data(bytesNoCopy: CChar(myBlob?.withUnsafeBytes), count: thisChunkSize, deallocator: false) – GTG101 Apr 19 '17 at 21:46
  • and I keep getting the error: Generic parameter 'ResultType' could not be inferred – GTG101 Apr 19 '17 at 21:47
  • I'd appreciate any help. – GTG101 Apr 21 '17 at 02:35