1

I am trying to subclass NSInputStream and NSOutputStream to keep track of the command I am passing to my server. This way, when I receive a response back from the server, I know which command it was a response to. When I try to set the command string in my subclass, I get an unrecognized selector error.

Subclasses:

PCFInputStream.h

#import <Foundation/Foundation.h>

@interface PCFInputStream : NSInputStream
@property (nonatomic, strong) NSString *command;
@end

PCFOutputStream.h

#import <Foundation/Foundation.h>

@interface PCFOutputStream : NSOutputStream
@property (nonatomic, strong) NSString *command;
@end

The .m files just have the command property synthesized so I can call setCommand:

Here is the class I am using these in:

 //instance vars in my class
 PCFInputStream *inputStream;
 PCFOutputStream *outputStream;


-(void)followClass:(id)sender
{
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef) SERVER_ADDRESS, PORT,     &readStream, &writeStream);
    inputStream = (__bridge_transfer PCFInputStream *)readStream;
    outputStream = (__bridge_transfer PCFOutputStream *)writeStream;
    [inputStream setDelegate:self];
    [outputStream setDelegate:self];
    [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [inputStream setCommand:[NSString stringWithFormat:@"%d,%@",[sender tag], [class classTitle]]];
    [inputStream open];
    NSString *str = [NSString stringWithFormat:@"_ADD_CLASS*%@*%@*%@*%@;", [class CRN], deviceToken, [class classLink],[class courseNumber]];
    [outputStream setCommand:str];
    [outputStream open];
}

Here is the error I get when it runs the line

[inputStream setCommand:[NSString stringWithFormat:@"%d,%@",[sender tag], [class classTitle]]];

-[__NSCFInputStream setCommand:]: unrecognized selector sent to instance 0x1e691170
2012-12-01 21:56:20.777 PCF[15610:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFInputStream setCommand:]: unrecognized selector sent to instance 0x1e691170'
*** First throw call stack:
(0x3a1b73e7 0x39043963 0x3a1baf31 0x3a1b964d 0x3a111208 0xfa267 0x39585047 0x39584ffb 0x39584fd5 0x3958488b 0x39584d79 0x394a3441 0x3a18c941 0x3a18ac39 0x3a18af93 0x3a0fe23d 0x3a0fe0c9 0x3743733b 0x394ee291 0x100005 0xe7d48)
libc++abi.dylib: terminate called throwing an exception

Can someone suggest a simple solution for me? I tried wrapping a NSStreamInput instance and my NSString* command into another class, but that doesn't work for my purposes.

Thanks!

kamran619
  • 531
  • 10
  • 32

2 Answers2

3

You can't just cast an NSInputStream to a PCFInputStream. The object has to be created as a PCFInputStream, and CFStreamCReatePairWithSocketToHost doesn't do that.

You can just attach a command property to NSInputStream in a category by using an associated object. Here's the category interface:

@interface NSInputStream (PCFCommand)
@property (nonatomic) NSString *pcf_command;
@end

You implement it like this:

#import <objc/runtime.h>

@implementation NSInputStream (PCFCommand)

static int kPCFCommandKey;

- (NSString *)pcf_command {
    return objc_getAssociatedObject(self, &kPCFCommandKey);
}

- (void)setPcf_command:(NSString *)pcf_command {
    objc_setAssociatedObject(self, &kPCFCommandKey, [pcf_command copy],
        OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Hi, your example is very helpful. I just don't know exactly where to put this declaration..I have never used categories before. – kamran619 Dec 02 '12 at 07:50
  • Choose New > File from the menu bar. Then choose the Objective-C category from the Cocoa Touch templates. Name the category `PCFCommand` and make it a category on `NSInputStream`. You'll need to import `NSInputStream+PCFCommand.h` in any file where you want to access the property. Repeat for `NSOutputStream`. – rob mayoff Dec 02 '12 at 07:52
  • Perfect. I just found this as you typed it out. The method 'objc_getAssociatedObject' is not found. Also the constant 'OBJC_ASSOCIATION_RETAIN_NONATOMIC' does not work either. – kamran619 Dec 02 '12 at 07:53
  • I have edited my answer. You need to import ``. – rob mayoff Dec 02 '12 at 08:01
  • Lol at you commenting exactly as I am about to say never mind. Haha thanks! – kamran619 Dec 02 '12 at 08:04
  • @robmayoff please help with this https://stackoverflow.com/questions/57195022/what-is-replacement-of-cfreadstreamcreateforstreamedhttprequest – Amit Jul 25 '19 at 05:52
1

CFStreamCreatePairWithSocketToHost creates a CFWriteStream and a CFReadStream. Those are toll-free bridged with NSOutputStream and NSInputStream respectively. So what you get back is an NSOutputStream and an NSInputStream. Whether you've declared subclasses of those or not makes no difference; those are the classes that CFStreamCreatePairWithSocketToHost creates.

If you really can't use wrapping for whatever reason, you'll need to reimplement the Core Foundation function to create your custom subclasses. In practice I'd recommend you just wrap the NS[Output/Input]Stream and use forwardingTargetForSelector: so that you can supply your wrappers anywhere one of the original objects is acceptable.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • I might have wrapped the classes incorrectly. Do you mind showing me a very simple example? I wrapped them in a NSObject initially – kamran619 Dec 02 '12 at 03:08
  • I couldn't get the wrappers working, but if I did wrap them, in the delegate method handleEvent, how do I get the command sent by the stream..since the handleEvent delegate method gives a NSStream object, not my custom object with the string command in it. – kamran619 Dec 02 '12 at 03:10