使用完成块,而不是返回一个变量 - iOS Swift

Posted

技术标签:

【中文标题】使用完成块,而不是返回一个变量 - iOS Swift【英文标题】:Using Completion Blocks, versus returning a variable - iOS Swift 【发布时间】:2017-02-01 18:43:33 【问题描述】:

我不确定我是否正确地使用了 Swift 中的函数。我发现在大多数函数中,我使用完成块来返回所需的变量,而不是直接返回它。这似乎是错误的,但我不确定。下面我将重点介绍代码示例。

功能结构: 通常使用

func fetchObject(name: String!, completionHandler:@escaping (_ success: Array<NSManagedObject>?) -> Void) 
    let objArray = ["one", "two", "three", name] //NOTE THIS IS A SILLY FUNCTION EXAMPLE BUT GETS THE STRUCTURE POINT ACROSS
    completionHandler(objArray)

不要用太多:

fun fetchObject(name: String!) -> Array<String>
    let objArray = ["one", "two", "three", name] //NOTE THIS IS A SILLY FUNCTION EXAMPLE BUT GETS THE STRUCTURE POINT ACROSS
    return objArray

我的大多数函数在调用函数时的样子:

fetchObjects(name: "example", completionHandler: (data:Array<String>?) -> Void in
    print("Data: \(data)")
    if let objects = data 
        //UPDATE UI
    


)

没有完成块时它们的外观:

let objects = fetchObjects(name: "example")
//DO stuff to objects

我遇到的问题是,通常每当我调用一个函数时,我的代码都依赖于它的有效返回,因此我几乎一直使用完成块。

这么多使用完成块是否正确?感觉不对,因为我几乎没有使用函数返回任何值

【问题讨论】:

使用完成处理程序的概念很好。完成处理程序的全部意义在于它们旨在允许函数异步执行,同时提供某种回调以在函数完成后触发。这不是您是否过于频繁地使用完成处理程序的问题。您对完成处理程序的使用通常与您的程序执行的需要异步完成的任务数量直接相关。 嗯,这很有帮助,所以我想我从根本上使用它们是错误的。相反,我应该使用返回值,然后检查返回值是否为零来填充我的 UI。 (显然对于时间敏感的功能) 就是这样。举个例子,在 ios 应用程序中有几个地方需要使用异步完成处理程序:UI 动画(例如 UIView.animate(withDuration:_:) )、网络调用(例如 URLRequest 甚至 Data(contentsOf:) ),也许还有 CoreData 操作(例如 NSManagedObjectContext.fetch(_:) )等任何具有足够长的处理时间以以人类明显的方式“冻结”用户的 UI。 所以我的一些用途确实包括 CoreData Fetch,但它是一个有保证的小型数据集 (1-15) 项。最好的做法是让它仍然异步吗?没有数据应用就没用了,同步还是异步? CoreData 实际上非常快,足以让我感觉很舒服地同步执行提取,尤其是对于这么小的数据集(1-15 项) 【参考方案1】:

使用完成处理程序的概念很好。完成处理程序的全部意义在于它们旨在允许函数异步执行,同时提供某种回调以在函数完成后触发。这不是您是否过于频繁地使用完成处理程序的问题。这是您执行异步任务的频率的问题。您对完成处理程序的使用通常与您的程序执行的需要异步完成的任务的数量直接相关。

例如,您可能希望在 iOS 应用中使用异步完成处理程序的几个地方:

UI 动画(例如 UIView.animate(withDuration:animations:)

网络调用(例如URLRequestData(contentsOf:options:)

CoreData 操作(例如 NSManagedObjectContext.fetch(_:))虽然通常即使这样也足够快,可以在没有完成处理程序的情况下同步执行

还有更多

任何具有足够长的处理时间以以人类可察觉的方式“冻结”用户界面的东西。

您的示例函数绝对是一个简单的返回值就足够的情况。

func fetchObject(name: String!, completionHandler: @escaping (_ success: Array<String>?) -> Void) 
    let objArray = ["one", "two", "three", name] //NOTE THIS IS A SILLY FUNCTION EXAMPLE BUT GETS THE STRUCTURE POINT ACROSS
    completionHandler(objArray)

可以转换为:

func fetchObject(name: String!) -> Array<String> 
    let objArray = ["one", "two", "three", name] //NOTE THIS IS A SILLY FUNCTION EXAMPLE BUT GETS THE STRUCTURE POINT ACROSS
    return objArray

这两个函数的结果完全相同。它们只是实现方式略有不同。如果函数看起来像这样,则完成处理程序(理论上)有意义的情况是:

func fetchObject(name: String!, completionHandler: @escaping (_ success: Array<String>?) -> Void) 
    DispatchQueue.main.async(execute:  () -> Void in
        let objArray = ["one", "two", "three", name] //NOTE THIS IS A SILLY FUNCTION EXAMPLE BUT GETS THE STRUCTURE POINT ACROSS
        completionHandler(objArray)
    )

因为函数使用不同的线程,所以使用完成处理程序更有意义,因为函数不再是同步的。那么在这之前哪里是有效的:

var result: Array<String> = Array<String>()
fetchObject(name: "name", completionHandler:  (otherResult) -> Void in
    result = otherResult ?? []
)
let first = result.first //first contains "one" when function is synchronous
//first contains nil when function is asynchronous (DispatchQueue)

如果我们异步执行函数的主体,那么这将不再有效,因为 let first = result.first 将在 result 成功分配给 otherResult 之前执行。

当然请记住,最终这完全取决于您。完成处理程序的好处是它们能够异步执行。在某些情况下,如果它们在功能上是不必要的,那么它只会增加不必要的冗长。不过,这确实取决于个人喜好,因为显然它们都可以实现以产生完全相同的结果。

让我知道这是否有意义。我可以根据要求进一步详细说明。

【讨论】:

这太完美了!谢谢!

以上是关于使用完成块,而不是返回一个变量 - iOS Swift的主要内容,如果未能解决你的问题,请参考以下文章

为啥我需要为某些完成块而不是其他块创建自己的线程安全版本?

快速返回完成块中的变量

使用 SWI 序言

IOS/Objective-C:从完成块中检索 NSArray

实训24 功能块的生成与调用 注意功能块的背景数据块 变量表最左侧的地址是系统自动生成的。前面讲 功能块与功能的区别是 功能块不能一个扫描周期完成,本功能块例子 确实不是一个扫描周期能完成,例如判

Swift:Xcode 6.1.1 更改由代码块执行而不是按钮触发的视图