我有一个应用程序在另一个线程上使用 NSStream 连接到服务器。如果用户决定注销,应用程序还会关闭连接。问题是我永远无法在用户断开连接时成功关闭流或线程。下面是我的代码示例,介绍了如何为我的网络创建线程并尝试关闭流:
+ (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;
}
尝试连接输入流时进行的调用:
[self performSelector:@selector(scheduleInThread:) onThread:[[self class] networkThread] withObject:nil waitUntilDone:YES];
任何意见是极大的赞赏。
混合静态变量和实例变量的方式令人困惑。你愿意这样做吗?如果您将其放入 NSOperation 中并使用 NSOperationQueue 运行它,我认为您会获得更清晰的封装。该操作将管理自己的异步线程,因此您不必这样做。另外,如果可以的话,我强烈建议使用 ARC。
一些注意事项:
- 确保设置流的委托并处理委托事件。您可能应该在操作内部执行此操作(使操作成为委托)并关闭流并在必要时完成操作。
- 除了 NSStreamStatusClosed 之外,流还可能存在其他失败情况,例如 NSStreamStatusNotOpen 等。您可能需要添加额外的处理,这可以通过侦听委托方法来完成。
- 您的代码可能无法正常工作,主要是因为您的 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)