【问题标题】:OutputStream hasSpaceAvailable never becomes 'true'OutputStream hasSpaceAvailable 永远不会变为“真”
【发布时间】:2013-11-15 06:40:55
【问题描述】:

在带有 ios 6.1.3 的 iPhone 上,我正在尝试写入 outputStream,而没有 NSRunLoop。我的流只是通过以下方式初始化:

    session = [[EASession alloc] initWithAccessory:accessory
                                       forProtocol:protocolString];
    if (session)
    {
        NSLog(@"opening the streams for this accessory");
        [[session inputStream] open];
        [[session outputStream] open];
        [session retain];
        streamReady = true;
        receivedAccPkt = true;
    }

然后,在代码的其他地方,当我尝试如下传输数据时:

uint8_t requestData[8] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
while (![[eas outputStream] hasSpaceAvailable]);
@synchronized(self) {
    len += [[eas outputStream] write:requestData maxLength:8];
}

'hasSpaceAvailable' 方法永远不会返回 true,因此代码卡住了。

为了发送数据,是否需要为输出流完成任何其他初始化任务?

【问题讨论】:

    标签: ios objective-c cocoa-touch external-accessory


    【解决方案1】:

    EASession 文档指出,您需要通过为其分配一个委托来配置流,该委托处理流事件并将其安排在运行循环中。

    使用运行循环使您能够驱动流而无需担心线程。例如,如果您通过 outputStream 属性写入输出流,则很可能有一个 internal 输入流(绑定到公共可见输出流),EASession 对象使用它来获取写入的字节。这个 internal 输入流(不可见)也可以使用与公开提供的输出流 outputStream 相同的运行循环。

    不要将内部输入流与公共输入流不匹配!所以基本上,有两个公共流和两个内部流。每个公共流在内部都有一个关联的挂件:输入流具有绑定的输出流,输出流具有绑定的输入流。

    但是,您可以避免对(输出)流使用运行循环:您只需要保证write:maxLength: 方法在私有线程上执行, EASession 的内部流不使用它。

    您可以获取您的私有线程,只需使用调度。你可以试试下面的代码:

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [outputStream open];
        const char* data = ...;
        int dataLength = ...;
        while (dataLength && !isCancelled) {
            int written = [outputStream write:(const uint8_t*)data maxLength:1];
            if (written > 0) {
                 dataLength -= written;
                 data += written;
            }
            else if (written < 0 )
            {
                // error occurred
                break;
            }
        }
        [outputStream close];
    });
    

    注意:如果先前对write:maxLength: 的调用返回零并且仍有数据要写入,这将阻塞线程。因此,内部流不能使用同一个线程,否则会出现死锁。

    当线程被阻塞时,取消阻塞变得困难。也许,您应该尝试运行循环方法。

    【讨论】:

    • 在流的上下文中使用运行循环是“推荐的”,而不是“要求”,对吗? (developer.apple.com/library/ios/documentation/cocoa/Conceptual/…)。我知道使用运行循环“更好”,但我认为我仍然应该能够使用轮询模式。我从运行循环切换到轮询的原因(我希望)在我发布的另一个问题中得到了解释:stackoverflow.com/questions/19768512/…
    • 你可以试试“拦截方式”。我的代码 sn-p 显示了如何完成此操作。
    • 有没有办法做我想要用 NSRunLoop 做的事情,或者它是不可能的?也就是写一个类似“ (NSMutableArray*) sendAndReceiveData:(NSMutableArray*)txData ”的函数,它会向设备发送数据,等待它的响应,然后使用runloops返回数据?
    • 运行循环方法采用异步编程风格。如果您尝试将异步模式包装到同步方法中,您会遇到某种“阻抗不匹配”。实际上,这意味着您需要阻塞外部同步方法,直到异步内部方法完成。虽然这是可能的,但这不是推荐的做法——或者更准确地说,它是一种“代码味道”。围绕运行循环方法的更好包装器是带有完成处理程序的异步方法。这听起来是不是更吸引人?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2013-12-27
    • 2016-10-12
    • 2018-10-29
    • 2019-02-05
    • 2015-09-09
    • 1970-01-01
    相关资源
    最近更新 更多