Swift - 可选参数未按预期工作

Swift - optional parameter not working as expected

我有一个方法可以迭代类别 class 的列表,最后从所有类别中迭代 return 项。 简单的代码如下所示:

func iterateMyCategoriesItems(item:(_ category:Category) -> Void)
{
    for category in allCategories
    {
        item(category)
    }
}

使用时:

iterateMyCategoriesItems { (category) in

   // doing something here with every category...
}

到目前为止一切顺利,但现在我想为此方法添加一个可选的完成,所以我将代码更改为:

func iterateMyCategoriesItems(item:(_ category:Category) -> Void, _ completion:(() -> Void)? = nil)
{
    for category in allCategories
    {
        item(category)
    }

    completion?()
}

但是现在当我尝试使用这样的方法时:

iterateMyCategoriesItems { (category) in

   // doing something here with every category...
}

编译器显示错误:

Missing argument for parameter 'item' in call.

所以,我做错了什么?

当您在函数末尾使用尾随 {} 时,编译器会自动将其分配给函数的最后一个参数(完成闭包),因此它认为缺少 items 参数你的电话

你可以这样使用它:

iterateMyCategoriesItems (items:{ (category) in

   // doing something here with every category...
})

或者如果你想完成

iterateMyCategoriesItems (items: { (category) in

   // doing something here with every category...
}) { _ in 
  //do things when completed
}

您应该能够确定您在此调用中使用了尾随闭包:

iterateMyCategoriesItems { (category) in

   // doing something here with every category...
}

Swift 编译器检测到这是一个尾随闭包,因此它将它与方法的最后一个参数(完成处理程序)匹配。

现在它已经完成了,它将继续寻找与第一个参数匹配的其他参数。 None 被发现所以它抱怨。

要解决此问题,您必须放弃使用尾随闭包并正确传递参数:

iterateMyCategoriesItems(items: { (category) in

   // doing something here with every category...
})

您也可以删除参数标签以使其更清晰。

Swift book describes the Trailing Closure如下:

If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead.

(添加了粗体。)

它清楚地将目标参数定位为 final

即使最后一个参数是可选的,这条规则似乎也适用。

//-> warning: closure parameter prior to parameters with default arguments will not be treated as a trailing closure
func iterateMyCategoriesItems(item:(_ category:Category) -> Void, _ someOptionalIntParam: Int? = nil)
{
    for category in allCategories
    {
        item(category)
    }
}

//-> error: missing argument for parameter 'item' in call
iterateMyCategoriesItems { (category) in

    // doing something here with every category...
}

如其他答案中所述,如果您想保持函数定义不变:

func iterateMyCategoriesItems(item:(_ category:Category) -> Void, _ completion:(() -> Void)? = nil)
{
    for category in allCategories
    {
        item(category)
    }

    completion?()
}

(这是另一个话题,但是如果你想写一个没有参数的闭包类型,你应该避免(Void)->Void。参见。)

你需要这样称呼它:

iterateMyCategoriesItems(item: { (category) in

    // doing something here with every category...
})

如果你想写 item 闭包总是作为尾随闭包,你可以像这样修改你的函数定义:

func iterateMyCategoriesItems(completion:(() -> Void)? = nil, item:(_ category:Category) -> Void)
{
    for category in allCategories
    {
        item(category)
    }

    completion?()
}

//Does not cause error
iterateMyCategoriesItems { (category) in

    // doing something here with every category...
}

但是,您可能不喜欢将 completion 闭包放在其他闭包之前。