1

I am trying to subclass NSMutableData to add the ability to subdata without copying. Here is code

@interface myMutableData : NSMutableData

- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range;

@end

@interface myMutableData()

@property (nonatomic, strong) NSData *parent;

@end

@implementation myMutableData

- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range
{
    unsigned char *dataPtr = (unsigned char *)[self bytes] + range.location;

    myMutableData *data = [[myMutableData alloc]     initWithBytesNoCopy:dataPtr length:range.length freeWhenDone:NO];

    data.parent = self;

    return data;
}

@end

But the problem is when I try to instantiate myMutableData, I got this error

"-initWithCapacity: only defined for abstract class.  Define -[myMutableData initWithCapacity:]!'"

Why? So inheritance does not work? Thanks

Michal
  • 15,429
  • 10
  • 73
  • 104
crab oz
  • 635
  • 7
  • 16

2 Answers2

3

NSData and NSMutableData are part of a class cluster. That means you need to do more work when subclassing to ensure that your subclass is fully valid.

In other words, don't subclass...

It's much easier for you to do what you want using a category, a wrapper or a helper / utility class. The best option is probably a wrapper which can return either the internal data directly or a specified range of the data.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • Thanks for the reply. Wrapper certainly looks a much more doable solution. Just curious, how would I do it with category? – crab oz Jun 04 '15 at 22:40
  • Again thanks for the reply. I decided to go with category as it seems cleaner. But I really appreciate your help. – crab oz Jun 05 '15 at 00:08
1

This calls for a category. However, a category cannot by default have properties and instance variables. Hence you need to #import <objc/runtime.h> and use associated objects to get and set value of parent.

@interface NSMutableData(myMutableData)

- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range;

@property (nonatomic, strong) NSData *parent;

@end

@implementation NSMutableData(myMutableData)

- (NSData *)subdataWithNoCopyingAtRange:(NSRange)range
{
    unsigned char *dataPtr = (unsigned char *)[self bytes] + range.location;

    NSMutableData *data = [[NSMutableData alloc]     initWithBytesNoCopy:dataPtr length:range.length freeWhenDone:NO];
    data.parent = self;
    return data;
}

-(NSData*)parent
{
    return objc_getAssociatedObject(self, @selector(parent));
}

-(void)setParent:(NSData *)parent
{
    objc_setAssociatedObject(self, @selector(parent), parent, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
lead_the_zeppelin
  • 2,017
  • 13
  • 23
  • Interesting solution. Thanks for the reply. Just wondering, is this considered a legal approach by Apple? – crab oz Jun 04 '15 at 22:38
  • 1
    Yes, it's perfectly legal, just don't abuse associated objects – Wain Jun 04 '15 at 22:52
  • Cool, this is great. Just two questions. In the solution, the parent property is exposed, can I still add it in class extension to keep it private? Also, I just did search around "associative reference", a lot of the examples use @dynamic. Should I use it? – crab oz Jun 04 '15 at 23:53
  • @craboz you won't be able to add `parent` to an extension since extension variables, methods MUST be implemented in the main implementation block, which is Apple's NSMutableData. Which is why you are stuck with categories for Apple's classes to which you don't have source code access. dynamic is a good idea. – lead_the_zeppelin Jun 05 '15 at 01:18