iOS 套接字输入流阻塞 UI,但未安排在主线程上

Max*_*wer 2 inputstream grand-central-dispatch ios swift

当我的应用程序通过网络输入流接收数据时,会在主线程上调用相应的streamDelegate。当接收大量数据时,UI 阻塞非常明显。我试图从 a 中触发所有相应的功能

DispatchQueue.global(qos: .whatever).sync()
Run Code Online (Sandbox Code Playgroud)

但尽管

inputStream.schedule(in: .current,forMode: .commonModes)
Run Code Online (Sandbox Code Playgroud)

在该队列中被调用,streamDelegate 仍然在 Main 上被调用?!

从我到现在为止阅读和理解的内容来看,.sync 无论如何都不会帮助我,因为它仍然会阻止 UI,因为它会等待完成,然后再将控制权交还给 UI。所以我对 .async 进行了同样的尝试。当所有流内容都在异步线程上工作时,outputStream 工作正常,但 inputStreams 的streamDelegate 不会触发。我没有收到回复。

我究竟做错了什么?

class AppDelegate {
  let mSocketClass = SocketClass()
  func application(_:){
    DispatchQueue.global(qos: .utility).async(){
      mSocketClass.setupNetworkCommunication()
      mSocketClass.sendStuff()
    }
  } 
}
Run Code Online (Sandbox Code Playgroud)

class SocketClass:NSObject {
  var mInputStream: InputStream!
  var mOutputStream: OutputStream!

  func setupNetworkCommunication(){
    var readStream: Unmanaged<CFReadStream>?
    var writeStream: Unmanaged<CFWriteStream>?
    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, "example.com" as CFString, 1234, &readStream, &writeStream)
    mInputStream = readStream!.takeRetainedValue()
    mOutputStream = writeStream!.takeRetainedValue()
    mInputStream.schedule(in: .current, forMode: .commonModes) //happens on thread other than Main
    mOutputStream.schedule(in: .current, forMode: .commonModes)
    mInputStream.open()
    mOutputStream.open()
    mInputStream.delegate = self
  }

  func sendStuff(){
    if mOutputStream.hasSpaceAvailable {
      mOutputStream.write(...)
    }
  }
}

extension SocketClass:StreamDelegate{
  func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    switch eventCode {
      case Stream.Event.hasBytesAvailable:
        (aStream as! InputStream).read(...) //happens on Main
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句:我不喜欢使用框架的解决方案。在我看来,解决方案是更改我不明白的一行。

谢谢!

Ste*_*cht 5

代码几乎没问题,但您需要在该线程上启动运行循环。

所以它应该看起来像:

DispatchQueue.global(qos: .utility).async(){
    self.mSocketClass.setupNetworkCommunication()
    RunLoop.current.run()
}
Run Code Online (Sandbox Code Playgroud)

请注意:

  • RunLoop.current.run()永远不会回来。应该是您设置中的最后一行。
  • 然后在后台线程上接收数据。如果您想在收到数据后更新您的用户界面,您必须使用DispatchQueue.main.async() { /* UI updates */ }

让我知道它对您有何作用。