如何在Swift中编写通用的apply()函数?

Ale*_*ter 13 functional-programming ios swift

有没有办法让以下工作在Swift 3?

 let button = UIButton().apply {
        $0.setImage(UIImage(named: "UserLocation"), for: .normal)
        $0.addTarget(self, action: #selector(focusUserLocation), 
                     for: .touchUpInside)
        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        $0.layer.cornerRadius = 5
     }
Run Code Online (Sandbox Code Playgroud)

apply<T>函数应该采用类型的闭包(T)->Void,运行它self进入它,然后简单地返回self.

另一种选择是使用像" =>" 这样的操作符(借用KotlinXtend语言的想法).

试图做这样的扩展NSObject:

extension NSObject {   
    func apply<T>(_ block: (T)->Void) -> T
    {
        block(self as! T)
        return self as! T
    }
}
Run Code Online (Sandbox Code Playgroud)

但它需要在闭包中显式声明参数类型:

let button = UIButton().apply { (it: UIButton) in
        it.setImage(UIImage(named: "UserLocation"), for: .normal)
        it.addTarget(self, action: #selector(focusUserLocation), 
                     for: .touchUpInside)
        ...
Run Code Online (Sandbox Code Playgroud)

这不方便,使整个想法不值得努力.该类型已在对象创建时指定,应该可以不显式重复.

谢谢!

Luc*_*tti 24

HasApply协议

首先让我们定义HasApply协议

protocol HasApply { }
Run Code Online (Sandbox Code Playgroud)

和相关的扩展

extension HasApply {
    func apply(closure:(Self) -> ()) -> Self {
        closure(self)
        return self
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来让我们NSObject遵守HasApply.

extension NSObject: HasApply { }
Run Code Online (Sandbox Code Playgroud)

而已

我们来试试吧

let button = UIButton().apply {
    $0.titleLabel?.text = "Tap me"
}

print(button.titleLabel?.text) // Optional("Tap me")
Run Code Online (Sandbox Code Playgroud)

注意事项

我不会使用NSObject(它是Objective-C做事方式的一部分,我认为它将在未来的某个时候删除).我宁愿选择类似的东西UIView.

extension UIView: HasApply { }
Run Code Online (Sandbox Code Playgroud)

  • 闭包的简单使用很好。(投票。)我想太多了。做得好 (2认同)

Ala*_* T. 6

我遇到了同样的问题,最后用运营商解决了这个问题:

infix operator <-< : AssignmentPrecedence
func <-<<T:AnyObject>(left:T, right:(T)->()) -> T
{
  right(left)
  return left
}

let myObject = UIButton() <-< { $0.isHidden = false }
Run Code Online (Sandbox Code Playgroud)


Lar*_*erg 5

有一个非常好的和简单的Cocoapods库,可以Then做到这一点。只有它使用then而不是apply。只需导入Then然后您就可以按照OP的要求进行操作:

import Then

myObject.then {
    $0.objectMethod()
}

let label = UILabel().then {
    $0.color = ...
}
Run Code Online (Sandbox Code Playgroud)

协议的实现方式如下:https : //github.com/devxoul/Then/blob/master/Sources/Then/Then.swift

extension Then where Self: Any {
    public func then(_ block: (Self) throws -> Void) rethrows -> Self {
        try block(self)
        return self
    }
Run Code Online (Sandbox Code Playgroud)