Objective c 铸造封闭件/砌块

Objective c 铸造封闭件/砌块,objective-c,closures,swift,block,Objective C,Closures,Swift,Block,在Objective-C中,我经常绕过街区。我经常使用它们来实现有助于避免将内容存储到实例变量中的模式,从而避免线程/计时问题 例如,我通过-[CAAnimation setValue:forKey://code>将它们指定给CAAnimation,以便在动画完成时执行块。(Objective-C可以将块视为对象;您还可以执行[someBlock copy]和[someBlock release]) 然而,尝试在Swift中与Objective-C一起使用这些模式似乎非常困难。(编辑:我们可以看

在Objective-C中,我经常绕过街区。我经常使用它们来实现有助于避免将内容存储到实例变量中的模式,从而避免线程/计时问题

例如,我通过
-[CAAnimation setValue:forKey://code>将它们指定给
CAAnimation
,以便在动画完成时执行块。(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'
}
我已经找到了一个解决办法,但它非常难看,IMHO:在我的桥接标题中,我有:

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

static inline void callBlockAsObject(id block)
{
    ((void(^)())block)();
}
现在我可以用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)
}
它可以工作,但是我需要为我想要使用的每一个块类型使用一个新函数,我正在修改编译器,这也不太好


那么,有没有一种方法可以完全快速地解决这个问题呢?

用函数类型参数化的泛型
块如何

class Block<T> {
  let f : T
  init (_ f: T) { self.f = f }
}
以及另一个示例,其中推断出
类型:

 11> var ar = [Block { (x:Int) in print ("Block: \(x)") }]
ar: [Block<(Int) -> ()>] = 1 value {
  [0] = {
    f = ...
  }
}
 12> ar[0].f(111)
Block: 111
11>var ar=[打印中的块{(x:Int)(“块:\(x)”)}]
ar:[Block()>]=1值{
[0] = {
f=。。。
}
}
12> ar[0].f(111)
区块:111

您需要做的就是使用
reinterpretCast
执行强制转换

(reinterpretCast(completion) as (@objc_block Void -> Void))()
来自REPL

  1> import Foundation
  2> var block : @objc_block Void -> Void = { println("test")}
block: @objc_block Void -> Void =
  3> var obj = reinterpretCast(block) as AnyObject // this is how to cast block to AnyObject given it have @objc_block attribute
obj: __NSMallocBlock__ = {}
  4> var block2 = reinterpretCast(obj) as (@objc_block Void -> Void)
block2: (@objc_block Void -> Void) =
  5> block2()
test
  6>  

我喜欢GoZoner的解决方案——将块包装在自定义类中——但既然您要求使用实际的“快速方式”在块和AnyObject之间执行强制转换,我只想回答这个问题:使用
unsafeBitCast进行强制转换
。(我猜这与陈慧琳(Bryan Chen)的《代码》重播《代码》(reinterpretCast)大致相同,该剧已不复存在。)

因此,在我自己的代码中:

typealias MyDownloaderCompletionHandler = @objc_block (NSURL!) -> ()
注:在Swift 2中,这将是:

typealias MyDownloaderCompletionHandler = @convention(block) (NSURL!) -> ()
以下是一个方向的演员阵容:

// ... cast from block to AnyObject
let ch : MyDownloaderCompletionHandler = // a completion handler closure
let ch2 : AnyObject = unsafeBitCast(ch, AnyObject.self)
// ... cast from AnyObject to block
let ch = // the AnyObject
let ch2 = unsafeBitCast(ch, MyDownloaderCompletionHandler.self)
// and now we can call it
ch2(url)
这是另一个方向的投射:

// ... cast from block to AnyObject
let ch : MyDownloaderCompletionHandler = // a completion handler closure
let ch2 : AnyObject = unsafeBitCast(ch, AnyObject.self)
// ... cast from AnyObject to block
let ch = // the AnyObject
let ch2 = unsafeBitCast(ch, MyDownloaderCompletionHandler.self)
// and now we can call it
ch2(url)

这里还有另一个解决方案,允许我们使用Objective-C转换值。它基于GoZoner将函数包装在类中的思想;不同之处在于我们的类是一个NSObject子类,因此可以在没有任何黑客攻击的情况下提供功能Objective-C块内存管理,并且可以直接用作AnyObject并移交给Objective-C:

typealias MyStringExpecter = (String) -> ()
class StringExpecterHolder : NSObject {
    var f : MyStringExpecter! = nil
}
下面介绍如何使用它包装函数并在需要AnyObject的位置传递:

func f (s:String) {println(s)}
let holder = StringExpecterHolder()
holder.f = f

let lay = CALayer()
lay.setValue(holder, forKey:"myFunction")
下面是以后如何提取函数并调用它:

let holder2 = lay.valueForKey("myFunction") as StringExpecterHolder
holder2.f("testing")

animation.setValue(completion,forKey:“completionClosure”)
在我的Xcode 6 beta 2项目中没有编译。@MartinR:谢谢,当时还在使用beta 1。已经更新了问题…情况变得更糟。很抱歉回答得太晚。这确实有效<代码>让completion=anim.valueForKey(“completionClosure”)作为?BlockVoid>;完成?.f()
虽然无法连接到Objective-C,但到目前为止,这是最好的解决方案。这是一个很好的解决方案。非常感谢这还能用吗?我收到错误“致命错误:无法在不同大小的类型之间取消安全广播”。对于
ch
var,我有以下内容:ch={()->字符串返回“works”}。这对你有用吗?@Jai我不知道;我已经很久没有使用
unsafeBitCast
了。我采用了GoZoner的通用包装解决方案。请注意,您可能仍然需要使用
@convention(block)
键入Alias完成处理程序类型,以便对其进行Objective-C内存管理。ok,它现在可以工作了。在您的代码中,有一个完成处理程序闭包,我不知道该放什么。所以我有一个->`let ch={()->字符串作为返回“yes it works”}`现在它失败了,错误是“致命错误:不能在不同大小的类型之间取消安全位广播”。只有当我把这个
让ch:mytypedeforeturnastring={()->字符串作为返回“yes”}
时,它才起作用。它似乎只有在明确地输入自定义类型defalias时才起作用。希望你在上面的代码中有这样的代码,以免让我头疼。无论如何,谢谢你1我的代码中确实有自定义的typealias。这是我代码的第一行。看到了吗?是的,我在那里看到了,但在这里没有看到。
let ch=//一个完成处理程序闭包
。请原谅我的noobism,但我不知道在
//一个完成处理程序闭包
“@objc_block”属性被删除之后要写什么代码;'@应改用“约定(块)”