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

时间:2017-05-07 11:27:20

标签: ios swift functional-programming

有没有办法让以下工作在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
     }

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
    }
}

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

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

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

谢谢!

5 个答案:

答案 0 :(得分:21)

HasApply协议

首先让我们定义HasApply协议

protocol HasApply { }

及相关扩展

extension HasApply {
    func apply(closure:(Self) -> ()) -> Self {
        closure(self)
        return self
    }
}

接下来让make NSObject符合HasApply

extension NSObject: HasApply { }

就是这样

让我们测试一下

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

print(button.titleLabel?.text) // Optional("Tap me")

考虑

  

我不会使用NSObject(它是Objective-C做事方式的一部分,我认为它将在未来的某个时候删除)。我更喜欢UIView之类的东西。

extension UIView: HasApply { }

答案 1 :(得分:5)

我遇到了同样的问题,最后用操作员解决了这个问题:

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

let myObject = UIButton() <-< { $0.isHidden = false }

答案 2 :(得分:5)

有一个名为Then的{​​{3}}就是这样做的。只是它使用then而不是apply。只需导入Then然后,您可以按OP要求:

import Then

myObject.then {
    $0.objectMethod()
}

let label = UILabel().then {
    $0.color = ...
}

以下是协议的实施方式:very good and simple Cocoapods library

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

答案 3 :(得分:1)

如果您对定制​​运营商不过敏,Alain会给出一个很好的答案。如果您不想使用它们,我能想出的最佳选择是:

String containerString = ....;
Data data = .....;
....
....
List<Command> commands = Arrays.asList(new ServerAdminCommand() , 
                         new DocumentRootCommand() /* etc...... */ );
commands.forEach(com -> com.execute(data,containerString));

然后允许您使用:

@discardableResult func apply<T>(_ it:T, f:(T)->()) -> T {
    f(it)
    return it
}

它并不完全相同,但总的来说效果非常好,并且具有T完全无拘无束的优势。这是一个明显做作的例子,但是:

let button = apply(UIButton()) { $0.setTitleText("Button") }

甚至允许:

func apply<T,R>(_ it:T, f:(T)->R) -> R {
    return f(it)
}

答案 4 :(得分:-1)

这是通用协议和扩展的示例。  我希望你能帮助你

protocol Container {
associatedtype ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }}

class Stack<S>: Container {
// original Stack<T> implementation
var items = [S]()
 func push(item: S) {
    items.append(item)}

 func pop() -> S {
    return items.removeLast()
}

// conformance to the Container protocol
 func append(item: S) {
    self.push(item: item)
}

var count: Int {
    return items.count
}

subscript(i: Int) -> S {
    return items[i]
}}

extension Stack {
var topItem: S? {
    return items.isEmpty ? nil : items[items.count - 1]
}}


var stringStack = Stack<String>()
var intStack    = Stack<Int>()