如何在另一个线程上正确打开和关闭 NSStream [英] How to properly open and close a NSStream on another thread

查看:43
本文介绍了如何在另一个线程上正确打开和关闭 NSStream的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序在另一个线程上使用 NSStream 连接到服务器.如果用户决定注销,应用程序也会关闭连接.问题是我永远无法在用户断开连接后成功关闭流或线程.下面是关于我如何为我的网络创建线程并尝试关闭流的代码示例:

I have an application that connects to a server using NSStream on another thread. The application also closes the connection should the user decide to log out. The problem is that I am never able to successfully close the stream or the thread upon having the user disconnect. Below is my code sample on how I approach creating a thread for my network and trying to close the stream:

+ (NSThread*)networkThread
{
    static NSThread *networkThread = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        networkThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkThreadMain:) object:nil];

        [networkThread start];
    });

    return networkThread;
}

+ (void)networkThreadMain:(id)sender
{
    while (YES)
    {
        @autoreleasepool {
            [[NSRunLoop currentRunLoop] run];
        }
    }
}

- (void)scheduleInThread:(id)sender
{
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [inputStream open];
}

- (void)closeThread
{    
    [inputStream close];
    [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [inputStream release];
    inputStream = nil;
}

尝试连接输入流时调用:

Call made when trying to connect the inputstream:

[self performSelector:@selector(scheduleInThread:) onThread:[[self class] networkThread] withObject:nil waitUntilDone:YES];

非常感谢任何建议.

推荐答案

混合静态变量和实例变量的方式令人困惑.你愿意这样做吗?如果你把它放在 NSOperation 中并使用 NSOperationQueue 运行它,我认为你会得到更清晰的封装.该操作将管理其自己的异步线程,因此您不必这样做.另外,如果可以,我强烈建议您使用 ARC.

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.

一些注意事项:

  1. 确保设置流的委托并处理委托事件.您可能应该在操作内部执行此操作(将操作设为委托)并在必要时关闭流并完成操作.
  2. 除了 NSStreamStatusClosed 之外,流可能还有其他失败条件,例如 NSStreamStatusNotOpen 等.您可能需要添加额外的处理,这可以通过侦听委托方法来完成.
  3. 您的代码可能无法正常工作,主要是因为您的 while 循环永远运行 runloop.你必须有突破的条件.NSOperation 为您提供了一些非常好的标准化方法.

#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

这篇关于如何在另一个线程上正确打开和关闭 NSStream的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆