写入后 NSOutputStream 因访问错误而崩溃(Objective-c)

Posted

技术标签:

【中文标题】写入后 NSOutputStream 因访问错误而崩溃(Objective-c)【英文标题】:NSOutputStream crashing with Bad Access after write (Objective-c) 【发布时间】:2013-10-03 16:11:16 【问题描述】:

我一直在尝试为我的 ios 应用程序启动并运行一个基本的 TCP 客户端,但遇到了一个我似乎无法解决的问题。

到目前为止,我可以连接,发送一条在服务器端收到但随后我的应用程序崩溃的消息。

客户端.h

#import <Foundation/Foundation.h>
@interface Client : NSObject <NSStreamDelegate>

    NSInputStream *inputStream;
    NSOutputStream *outputStream;


-(void)initNetworkCommunication;
-(void)send:(NSString*)message;
@end

客户.m

#import "Client.h"

@implementation Client

- (void)initNetworkCommunication 
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"10.0.1.51", 7769, &readStream, &writeStream);
    inputStream = ( NSInputStream *)CFBridgingRelease(readStream);
    outputStream = ( NSOutputStream *)CFBridgingRelease(writeStream);

    [inputStream setDelegate:self];
    [outputStream setDelegate:self];

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    [inputStream open];
    [outputStream open];


- (void)send:(NSString*)message

    NSData *data = [[NSData alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding]];
    [outputStream write:[data bytes] maxLength:[data length]];


- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent 
    NSLog(@"stream event %i", streamEvent);

    switch (streamEvent) 

        case NSStreamEventOpenCompleted:
            NSLog(@"Stream opened");
            break;

        case NSStreamEventHasBytesAvailable:
            if (theStream == inputStream) 

                uint8_t buffer[1024];
                int len;

                while ([inputStream hasBytesAvailable]) 
                    len = [inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0) 

                        NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];

                        if (nil != output) 
                            NSLog(@"server said: %@", output);
                        
                    
                
            
            break;

        case NSStreamEventErrorOccurred:
            NSLog(@"Can not connect to the host!");
            break;

        case NSStreamEventEndEncountered:
            NSLog(@"End Encountered!");
            [theStream close];
            [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            theStream = nil;
            break;

        default:
            NSLog(@"Unknown event");
    


@end

控制台中的输出是

2013-10-03 17:01:38.542 MMORPG[6076:70b] stream event 1
2013-10-03 17:01:38.543 MMORPG[6076:70b] Stream opened
2013-10-03 17:01:43.495 MMORPG[6076:70b] stream event 4
2013-10-03 17:01:43.495 MMORPG[6076:70b] Unknown event

看起来,我的消息已发送,我收到了流事件 #4,然后我遇到了错误的访问崩溃。问题是我不知道它在访问时遇到了什么问题?

任何帮助将不胜感激!

【问题讨论】:

你能发布崩溃的堆栈跟踪吗? EXC_BAD_ACCESS 崩溃通常表示内存管理错误。使用 NSZombies 工具可能会帮助您找出问题所在。 启用僵尸后,我在输出中收到此额外消息 2013-10-03 17:48:43.989 MMORPG[6332:70b] *** -[Client respondsToSelector:]: message sent to deallocated instance 0x8ecffc0 这可能与我的对象实现的 NSStreamDelegate 有关吗? 是的,如果你的 NSStream 的委托(一个客户端实例)在流仍然存在并打开时被释放,你会遇到你描述的崩溃。您需要确保某些内容对客户端具有强引用,否则请确保在释放客户端之前关闭并释放流。 我多么愚蠢!好电话,在我的视图控制器中缺少一个强大的参考。我真的不知道我是怎么忽略的! 很高兴听到这个消息。我把我的评论变成了一个真实的答案,如果你愿意接受的话。 【参考方案1】:

问题在于NSStream 保留了对其委托的assign/unsafe_unretained 引用。如果委托在流关闭和释放之前被释放,则流将尝试调用其现在已解除分配的委托的方法,从而导致崩溃。解决方案是确保某些其他对象对客户端具有强引用,防止其提前释放,或者确保在其委托被释放之前关闭并释放流。

【讨论】:

以上是关于写入后 NSOutputStream 因访问错误而崩溃(Objective-c)的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中将字符串写入 NSOutputStream

Server 服务因下列错误而停止: 拒绝访问。

无法调配 NSOutputStream 的 write:MaxLength:

iOS开发系列-NSOutputStream

将 AudioUnit 回调与 NSOutputStream 同步

数据流作业在 BigQuery 写入失败并出现后端错误