Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/108.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 发出多个异步请求,但只等待一个_Ios_Swift_Asynchronous - Fatal编程技术网

Ios 发出多个异步请求,但只等待一个

Ios 发出多个异步请求,但只等待一个,ios,swift,asynchronous,Ios,Swift,Asynchronous,我有一个关于异步请求的问题。我想从网络上的不同来源获取数据。每个来源可能都有我想要的数据,但我事先不知道。因为我只需要这些信息一次,一旦一个信息源给了我所需的数据,我就不在乎其他信息源了。我该怎么做呢? 我想用didSet做,只设置一次,类似这样: var dogPicture:dogPicture?=零{ 迪塞特{ //对这幅画做些什么 } } func findPictureOfDog(u)来源)->狗图片?{ 源中的源{ 让task=URL.Session.shared.dataTask

我有一个关于异步请求的问题。我想从网络上的不同来源获取数据。每个来源可能都有我想要的数据,但我事先不知道。因为我只需要这些信息一次,一旦一个信息源给了我所需的数据,我就不在乎其他信息源了。我该怎么做呢? 我想用didSet做,只设置一次,类似这样:


var dogPicture:dogPicture?=零{
迪塞特{
//对这幅画做些什么
}
}
func findPictureOfDog(u)来源)->狗图片?{
源中的源{
让task=URL.Session.shared.dataTask(带:source){(数据、响应、错误)在
//错误处理。。。
如果data.isWhatIWanted()&&dogPicture==nil{
dogPicture=data.getPicture()
}
}
task.resume()
}
}
来源=[“yahoo.com”、“google.com”、“pinterest.com”]
findPictureOfDog(来源)
但是,如果我可以等到
findPictureOfDog()
完成,这将非常有帮助,因为根据我是否找到什么,我必须要求用户提供更多输入

我不知道如何用上面的方法来做,因为如果我没有找到任何东西,didSet将永远不会被调用,但我应该向用户索要一张图片

另外:
iswhatiwant()
相当昂贵,因此,如果有一种方法可以在我找到一个DogPicture后中止处理程序的执行,那就太好了

我希望我说清楚了,希望有人能帮我解决这个问题


向您致以最诚挚的问候并感谢您抽出宝贵的时间

如果您只关心一项任务,请使用完成处理程序,如果找不到图片,请致电
completion(nil)

var dogPicture : DogPicture? 

func findPictureOfDog(_ sources, completion: @escaping (DogPicture?) -> Void) {
  for source in sources {
    let task = URL.Session.shared.dataTask(with: source) { (data, response, error) in
      // error handling ...
      if data.isWhatIWanted() && dogPicture == nil {
        let picture = data.getPicture()
        completion(picture)
      }
    }
    task.resume()
  }
}

sources = ["yahoo.com", "google.com", "pinterest.com"] 

findPictureOfDog(sources) { [weak self] picture in
   if let picture = picture {
      self?.dogPicture = picture
      print("picture set")
   } else {
      print("No picture found")
   }
}

您可以使用
DispatchGroup
在所有请求返回后运行检查:

func findPictureOfDog(_ sources: [String]) -> DogPicture? {
  let group = DispatchGroup()

  for source in sources {
    group.enter()
    let task = URLSession.shared.dataTask(with: source) { (data, response, error) in
      // error handling ...
      if data.isWhatIWanted() && dogPicture == nil {
        dogPicture = data.getPicture()
      }
      group.leave()
    }
    task.resume()
  }

  group.notify(DispatchQueue.main) {
    if dogPicture == nil {
        // all requests came back but none had a result.
    }
  }
}
有几件事:

  • 首先,我们处理的是异步进程,因此您不应该
    返回
    DogPicture
    ,而应该使用完成处理程序模式。例如,而不是:

    func findPictureOfDog(_ sources: [String]) -> DogPicture? {
        ...
        return dogPicture
    }
    
    相反,您可能会执行以下操作:

    func findPictureOfDog(_ sources: [String], completion: @escaping (Result<DogPicture, Error>) -> Void) {
        ...
        completion(.success(dogPicture))
    }
    
  • 虽然上面提到了“您不能仅从异步进程返回值”,但相关的观察结果是,您不希望依赖属性作为触发器在进程完成时发出信号。所有“第一个进程完成时”逻辑都应该在
    findPictureOfDog
    例程中,并在完成时调用完成处理程序

    我建议不要在这个过程中使用属性和它们的观察者,因为这会引发关于如何同步访问以确保线程安全等问题。完成处理程序是明确的,可以避免这些次要问题

  • 您提到,
    是您想要的
    在计算上非常昂贵。这有两个含义:

    • 如果它在计算上很昂贵,那么您可能不希望在
      dataTask(with:completionHandler:)
      completionHandler中同步调用它,因为这是一个串行队列。每当处理串行队列(无论是主队列、网络会话串行队列还是任何自定义串行队列)时,您通常希望尽快进出(以便队列可以自由地继续处理其他任务)

      例如,让我们想象一下,谷歌的请求最先出现,但此时您还不知道,它没有包含您想要的内容,
      iswhatiwant
      正在缓慢地检查结果。让我们想象一下,在这段时间里,雅虎的请求进来了。如果同步调用
      iswhatiwant
      ,Yahoo请求的结果将无法开始检查其结果,直到Google请求失败,因为您正在对此串行队列执行同步调用

      我建议您可能希望在收到结果时开始检查,而不是等待其他结果。为此,您需要一个
      iswhatiwant
      的格式副本,该格式副本相对于网络串行队列异步运行

    • isWhatIWanted
      是一个可取消的过程吗?理想情况下是这样,因此如果雅虎镜像成功,它可以取消现在不必要的Pinterest
      iswhatiwant
      。取消网络请求很容易,但更可能的是,我们真正想要取消的是这个昂贵的
      iswhatiwant
      过程。但是我们不能在没有看到你在那里做什么的情况下对此发表评论

      但是,让我们假设您正在通过
      VNCoreMLRequest
      对象执行对象分类。因此,您可能会在找到第一个匹配项后立即处理任何挂起的请求

  • 在您的示例中,您列出了三个来源。可能有多少来源?在处理此类问题时,通常需要限制并发程度。例如,假设在生产环境中,您要查询100个不同的源,您可能希望确保在任何给定的时间内运行的源不超过(比如)六个,因为内存和CPU限制

  • 尽管如此,所有这些考虑(异步、可取消、受限并发)似乎都在乞求基于
    操作的解决方案

    因此,为了回答您的主要问题,我们的想法是编写一个例程,在源代码中进行迭代,并在第一次成功时调用主完成处理程序,同时确保防止任何后续/并发请求调用完成处理程序:

    • 您可以保存对完成处理程序的本地引用
    • 一旦成功找到合适的图像,您可以:

      • 调用保存的完成处理程序
      • nil
        您保存的引用(因此,如果您有其他请求已在大约
        findPictureOfDog(sources: [String]) { result in
            switch result {
            case .success(let dogPicture): ...
            case .failure(let error): ...
            }
        }
        
        // but don’t try to access the DogPicture or Error here
        
        func findPictureOfDog(_ sources: [String], completion: @escaping DogPictureCompletion) {
            var firstCompletion: DogPictureCompletion? = completion
            let synchronizationQueue: DispatchQueue = .main         // note, we could have used any *serial* queue for this, but main queue is convenient
        
            let completionOperation = BlockOperation {
                synchronizationQueue.async {
                    // if firstCompletion not nil by the time we get here, that means none of them matched
                    firstCompletion?(.failure(DogPictureError.noneFound))
                }
                print("done")
            }
        
            for source in sources {
                let url = URL(string: source)!
                let operation = DogPictureOperation(url: url) { result in
                    if case .success(_) = result {
                        synchronizationQueue.async {
                            firstCompletion?(result)
                            firstCompletion = nil
                            Queues.shared.cancelAllOperations()
                        }
                    }
                }
                completionOperation.addDependency(operation)
                Queues.shared.processingQueue.addOperation(operation)
            }
        
            OperationQueue.main.addOperation(completionOperation)
        }