铸造闭合/块

Dar*_*ust 15 closures block objective-c swift

在Objective-C中,我经常传递块.我经常使用它们来实现模式,以帮助避免将内容存储到实例变量中,从而避免线程/时序问题.

例如,我将它们分配给一个CAAnimationvia,-[CAAnimation setValue:forKey:]这样我就可以在动画结束时执行该块.(Objective-C中可以把块作为对象,你也可以做[someBlock copy][someBlock release].)

但是,尝试在Swift和Objective-C中使用这些模式似乎非常困难.(编辑:我们可以看到语言仍在变化:已经调整了代码,因此它适用于Xcode6-beta2,之前的版本适用于Xcode6-beta1.)

例如,我无法转换AnyObject回块/闭包.以下产生编译器的错误:

override func animationDidStop(anim: CAAnimation!, finished flag: Bool)
{
    let completion : AnyObject! = anim.valueForKey("completionClosure")
    (completion as (@objc_block ()->Void))()
    // Cannot convert the expression's type 'Void' to type '@objc_block () -> Void'
}
Run Code Online (Sandbox Code Playgroud)

我找到了一个解决方法,但它非常难看,恕我直言:在我的桥接标题中,我有:

static inline id blockToObject(void(^block)())
{
    return block;
}

static inline void callBlockAsObject(id block)
{
    ((void(^)())block)();
}
Run Code Online (Sandbox Code Playgroud)

现在我可以在Swift中做到这一点:

func someFunc(completion: (@objc_block ()->Void))
{
    let animation = CAKeyframeAnimation(keyPath: "position")
    animation.delegate = self
    animation.setValue(blockToObject(completion), forKey: "completionClosure")
    …
}

override func animationDidStop(anim: CAAnimation!, finished flag: Bool)
{
    let completion : AnyObject! = anim.valueForKey("completionClosure")
    callBlockAsObject(completion)
}
Run Code Online (Sandbox Code Playgroud)

它可以工作,但是我需要为我想要使用的每种块类型使用一个新函数,而且我在编译器上进行攻击也不是很好.

那么有没有办法以纯粹的Swift方式解决这个问题?

GoZ*_*ner 10

Block使用函数类型参数化的泛型怎么样?

class Block<T> {
  let f : T
  init (_ f: T) { self.f = f }
}
Run Code Online (Sandbox Code Playgroud)

分配其中一个; 它将是一个子类型,AnyObject因此可以分配到字典和数组中.这对于尾随闭包语法来说似乎并不太繁琐.正在使用:

  5> var b1 = Block<() -> ()> { print ("Blocked b1") }
b1: Block<() -> ()> = {
  f = ...
}
  6> b1.f()
Blocked b1
Run Code Online (Sandbox Code Playgroud)

以及Block推断出类型的另一个例子:

 11> var ar = [Block { (x:Int) in print ("Block: \(x)") }]
ar: [Block<(Int) -> ()>] = 1 value {
  [0] = {
    f = ...
  }
}
 12> ar[0].f(111)
Block: 111
Run Code Online (Sandbox Code Playgroud)


mat*_*att 9

我喜欢GoZoner的解决方案 - 将块包装在一个自定义类中 - 但是由于你要求实际的"Swift方式"来执行块和AnyObject之间的转换,我只是给出了这个问题的答案:cast with unsafeBitCast.(我猜这与布莱恩陈的差不多一样reinterpretCast,不再存在.)

所以,在我自己的代码中:

typealias MyDownloaderCompletionHandler = @objc_block (NSURL!) -> ()
Run Code Online (Sandbox Code Playgroud)

注意:在Swift 2中,这将是:

typealias MyDownloaderCompletionHandler = @convention(block) (NSURL!) -> ()
Run Code Online (Sandbox Code Playgroud)

这是一个方向的演员:

// ... cast from block to AnyObject
let ch : MyDownloaderCompletionHandler = // a completion handler closure
let ch2 : AnyObject = unsafeBitCast(ch, AnyObject.self)
Run Code Online (Sandbox Code Playgroud)

这是另一个方向的演员:

// ... cast from AnyObject to block
let ch = // the AnyObject
let ch2 = unsafeBitCast(ch, MyDownloaderCompletionHandler.self)
// and now we can call it
ch2(url)
Run Code Online (Sandbox Code Playgroud)