菜鸟教程小白 发表于 2022-12-13 13:51:06

ios - 与 NSOperation 和 NSOperationQueue (Swift) 相互依赖的 NSURLSession 任务


                                            <p><p><strong>问题</strong>:
我想在 Swift 中开发一个 iOS 应用程序,它在登录后立即执行初始加载。序列(通过 NSURLSession 的基于 REST 的调用)如下所示:</p>

<ol>
<li>用用户账号登录 -> 异步响应返回<em>userId</em></li>
<li>获取 <em>userId</em> 的国家 -> 异步响应返回 <em>countryId</em> 的</li>
<li>获取 <em>countryId</em> 的产品 -> ...等...</li>
</ol>

<p>基本上我想找到一种优雅的方式来实现这样的序列。</p>

<p><strong>方法</strong>:
首先,我只是在另一个的完成处理程序中调用新的(依赖的)REST 调用。但是如果需要执行很多调用并且依赖级别比上面描述的要多,那么代码看起来有点乱......</p>

<p>我在 WWDC 2015 上遇到了关于 NSOperations 的 session ,并认为这可能是一个好主意,因为有些人可以很容易地定义依赖关系。
不幸的是,Apple 提供的示例代码没有给出上述问题的答案……是吗(我没有得到它?)?
在玩操作操作时,我无法考虑<strong>在创建不同操作(LoginOperation、CountryOperation(依赖于 LoginOperation)、ProductOperation(依赖于 CountryOperation)等时如何解决初始化问题。)</strong></p>

<p>我发现这些帖子很有帮助,但我仍然不了解如何最好地解决我描述的问题:
<a href="https://stackoverflow.com/questions/32322386/how-to-download-multiple-files-sequentially-using-nsurlsession-downloadtask-in-s/32322564" rel="noreferrer noopener nofollow">How To Download Multiple Files Sequentially using NSURLSession downloadTask in Swift</a>
<a href="https://stackoverflow.com/questions/33633872/chaining-nsoperation-pass-result-from-an-operation-to-the-next-one" rel="noreferrer noopener nofollow">Chaining <code>NSOperation</code> : Pass result from an operation to the next one</a>
<a href="https://stackoverflow.com/questions/21198404/nsurlsession-with-nsblockoperation-and-queues/21205992#21205992" rel="noreferrer noopener nofollow">NSURLSession with NSBlockOperation and queues</a> </p>

<p><strong>困难</strong>:
在另一个操作 A 成功完成并返回将由操作 B 使用的结果时初始化操作 B。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>如果仍然相关,我可能会有答案。</p>

<p>这里的问题是你试图推迟初始化直到第一个操作完成。这是一个死胡同。操作<strong>意味着</strong>要尽早创建,并且使用依赖项来保证顺序。</p>

<p>所以让我们讨论一些解决这个极其常见的问题的方法。</p>

<p>(您可以注意到这些示例是用 WWDCsession 风格编写的。这实际上是因为我使用了基于该演讲的 <a href="https://github.com/AdvancedOperations/Operations" rel="noreferrer noopener nofollow">Operations library</a>。如果您对基于操作的架构感兴趣,请查看它。 )</p>

<h3>1。使用一些外部可变共享状态(Cocoa-way)</h3>

<p>这基本上意味着,对于您的示例,您有一些 <code>UserController</code>,您将其实例传递给 <code>LoginOperation</code> 和 <code>CountryOperation</code> . <code>LoginOperation</code> 将 <code>userId</code> 写入该 Controller ,<code>CountryOperation</code> 从中读取:</p>

<pre><code>class UserController {
    var userID: String?
}

class LoginOperation: Operation {

    let userController: UserController

    init(userController: UserController) {
      self.userController = userController
    }

    override func execute() {
      // do your networking stuff
      userController.userID = receivedUserID
      finish()
    }

}

class CountryOperation: Operation {

    let userController: UserController

    init(userController: UserController) {
      self.userController = userController
    }

    override func execute() {
      let userID = userController.userID
      // continue
    }

}
</code></pre>

<p>然后:</p>

<pre><code>let queue = OperationQueue()
let userController = UserController()
let login = LoginOperation(userController: userController)
let country = CountryOperation(userController: userController)
country.addDependency(login)
queue.addOperation(login)
queue.addOperation(country)
</code></pre>

<p>该解决方案的问题在于它非常复杂且难以测试。而且 Swift 也不太喜欢可变共享状态。</p>

<h3>2。使用函数而不是值来初始化操作(Swifty-way)</h3>

<p>嗯,这不是显而易见但非常优雅的解决方案。如您所见,值在初始化时被<strong>复制</strong>。我们真正需要的是检索所需的执行时间值(与前面的示例一样),但没有如此紧密的耦合。好吧,这很容易做到 - 只需将一个函数传递给初始化程序!</p>

<pre><code>class LoginOperation: Operation {

    private(set) var userID: String?

    override func execute() {
      // do your networking stuff
      let receivedUserID = &#34;1&#34;
      userID = receivedUserID
      finish()
    }

}

class CountryOperation: Operation {

    private let getUserID: () -&gt; String

    init(getUserID: () -&gt; String) {
      self.getUserID = getUserID
    }

    override func execute() {
      /* Because this operation depends on `LoginOperation`,
      `getUserID()` is called after `LoginOperation` is finished. */
      let userID = self.getUserID()
      // continue
    }

}
</code></pre>

<p>然后:</p>

<pre><code>let queue = OperationQueue()
let login = LoginOperation()
// Force-unwrap is no good, of course, but for this example it&#39;s ok
let country = CountryOperation(getUserID: { login.userID! })
country.addDependency(login)
queue.addOperation(login)
queue.addOperation(country)
</code></pre>

<p>如您所见,没有紧密耦合,没有共享可变状态(<code>CountryOperation</code> 只能读取,不能写入——这很好)。而且测试起来非常简单:只需将您想要的任何内容传递给 <code>CountryOperation</code> 初始化程序,您根本不需要 <code>LoginOperation</code> 来测试它!</p>

<p>实际上,通过这种方法,<code>NSOperation</code> 的主要目标得以实现:独特的、抽象的工作。 <code>CountryOperation</code> 本身对 <code>LoginOperation</code> 以及任何其他共享实例一无所知。</p>

<p>我实际上在我的项目中经常使用这种方法,并且效果非常好。</p>

<p>如果您有任何其他问题,请回复:)</p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 与 NSOperation 和 NSOperationQueue (Swift) 相互依赖的 NSURLSession 任务,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/35035478/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/35035478/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 与 NSOperation 和 NSOperationQueue (Swift) 相互依赖的 NSURLSession 任务