Question

I am trying to write a subclass of NSOutputStream to perform a very simple function - keep track of the total number of bytes sent to the stream. However, I am running into an unexpected problem initializing an instance of the function. Here is the code:

#import <Foundation/Foundation.h>

@interface TrackingOutputStream : NSOutputStream {
  unsigned long long bytesWritten;
}

@property (readonly) unsigned long long bytesWritten;

@end

---------------------------

#import "TrackingOutputStream.h"

@implementation TrackingOutputStream
@synthesize bytesWritten;

- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length {
  NSInteger written = [super write:buffer maxLength:length];
  bytesWritten += written;
  return written;
}

@end

However, when I try to initialize this class:

TrackingOutputStream *os = [[[TrackingOutputStream alloc] initToFileAtPath:@"/tmp/test" append:NO] autorelease];

I get the following error:

-[TrackingOutputStream initToFileAtPath:append:]: unrecognized selector sent to instance 0x101a187e0

I've tried adding an explicit constructor to the class that calls super, but it doesn't make any difference (as it shouldn't).

Was it helpful?

Solution

NSOutputStream has very specific subclassing requirements that are documented in the class's documentation.

Note that the documentation explicitly states that you must implement the appropriate initializers fully. I.e. you can't subclass to change the behavior as you described. At least, not easily.

Instead, create a class whose instances wrap an instance of NSOutputStream and add the behavior you desire.

OTHER TIPS

If you're working with an API that expects an instance of NSOutputStream, it can be cumbersome to implement all the methods of NSOutputStream in order to forward them to the wrapped (delegate) instance. You can use method-forwarding approach that will allow you to add behavior without writing all the wrapper methods. This involves writing a simple implementation of forwardingTargetForSelector: and respondsToSelector:


- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (class_respondsToSelector([self class], aSelector)) { return self; }
    if ([self.delegate respondsToSelector:aSelector]) { return self.delegate; }
    return [super forwardingTargetForSelector:aSelector];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if (class_respondsToSelector([self class], aSelector)) { return YES; }
    if ([self.delegate respondsToSelector:aSelector]) { return YES; }
    return [super respondsToSelector:aSelector];
}

For a longer, detailed description please see the blog post on using Objective-C duck-typing to more easily subclass NSOutputStream. Or check out the sample on https://github.com/jwb/ObjC-DuckType

As an additional note, it should be possible to retrieve info on the written data using the - propertyForKey: of NSStream. Check out the NSStreamDataWrittenToMemoryStreamKey property key.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top