The way you're mixing static and instance variables is confusing. Are you married to doing it that way? If you put this inside an NSOperation and ran it using an NSOperationQueue I think you'd get much cleaner encapsulation. The operation will manage its own async thread so you don't have to. Also, I highly recommend using ARC if you can.
A few notes:
- Make sure to set the stream's delegate and handle delegate events. You should probably do that inside the operation (make the operation the delegate) and close the stream and finish the operation when necessary.
- There may be other failure conditions for the stream besides NSStreamStatusClosed, such as NSStreamStatusNotOpen, etc. You will probably need to add additional handling, which can be done by listening to the delegate methods.
- Your code is probably not working right mainly because your while loop runs the runloop forever. You have to have conditions in which to break out. NSOperation gives you some pretty good standardized ways of doing this.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface AsyncStreamOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
#import "AsyncStreamOperation.h"
@interface AsyncStreamOperation ()
@property (atomic, strong) AsyncStreamOperation *config;
@property (atomic, strong) NSInputStream *stream;
@property (atomic, assign, getter=isExecuting) BOOL executing;
@property (atomic, assign, getter=isFinished) BOOL finished;
@end
@implementation AsyncStreamOperation
@synthesize executing = _executing;
@synthesize finished = _finished;
- (instancetype)initWithStream:(NSInputStream *)stream
{
self = [super init];
if(self) {
_stream = stream;
}
return self;
}
- (BOOL)isAsynchronous
{
return YES;
}
- (BOOL)isExecuting
{
@synchronized (self) {
return _executing;
}
}
- (void)setExecuting:(BOOL)executing
{
@synchronized (self) {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
- (BOOL)isFinished
{
@synchronized (self) {
return _finished;
}
}
- (void)setFinished:(BOOL)finished
{
@synchronized (self) {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
}
- (void)start
{
// Get runloop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
// Schedule stream
[self.stream scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
[self.stream open];
// Loop until finished
// NOTE: If -cancel is not called, you need to add your own logic to close the stream so this loop ends and the operation completes
while(self.executing && !self.finished && !self.cancelled && self.stream.streamStatus != NSStreamStatusClosed) {
@autoreleasepool {
// Maximum speed once per second or CPU goes through the roof
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
}
self.executing = NO;
self.finished = YES;
}
- (void)cancel
{
[super cancel];
[self.stream close];
self.executing = NO;
self.finished = YES;
}
@end