Cast closures/blocks

前端 未结 4 1819
粉色の甜心
粉色の甜心 2020-12-05 15:35

In Objective-C, I often pass around blocks. I use them very often to implement patterns that help avoid storing stuff into instance variables, thus avoiding threading/timing

相关标签:
4条回答
  • 2020-12-05 15:44

    How about a generic Block parameterized with the function type?

    class Block<T> {
      let f : T
      init (_ f: T) { self.f = f }
    }
    

    Allocate one of these; it will be a subtype of AnyObject and thus be assignable into dictionaries and arrays. This doesn't seem too onerous especially with the trailing closure syntax. In use:

      5> var b1 = Block<() -> ()> { print ("Blocked b1") }
    b1: Block<() -> ()> = {
      f = ...
    }
      6> b1.f()
    Blocked b1
    

    and another example where the Block type is inferred:

     11> var ar = [Block { (x:Int) in print ("Block: \(x)") }]
    ar: [Block<(Int) -> ()>] = 1 value {
      [0] = {
        f = ...
      }
    }
     12> ar[0].f(111)
    Block: 111
    
    0 讨论(0)
  • 2020-12-05 15:52

    I like GoZoner's solution - wrap the block in a custom class - but since you asked for the actual "Swift way" to perform the cast between a block and an AnyObject, I'll just give the answer to that question: cast with unsafeBitCast. (I'm guessing that this is more or less the same as Bryan Chen's reinterpretCast, which no longer exists.)

    So, in my own code:

    typealias MyDownloaderCompletionHandler = @objc_block (NSURL!) -> ()
    

    Note: in Swift 2, this would be:

    typealias MyDownloaderCompletionHandler = @convention(block) (NSURL!) -> ()
    

    Here's the cast in one direction:

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

    Here's the cast back in the other direction:

    // ... cast from AnyObject to block
    let ch = // the AnyObject
    let ch2 = unsafeBitCast(ch, MyDownloaderCompletionHandler.self)
    // and now we can call it
    ch2(url)
    
    0 讨论(0)
  • 2020-12-05 15:58

    Here's yet another solution, allowing us to cast to exchange values with Objective-C. It builds on GoZoner's idea of wrapping the function in a class; the difference is our class is an NSObject subclass, and can thus give the function Objective-C block memory management without any hackery, and can be directly used as an AnyObject and handed over to Objective-C:

    typealias MyStringExpecter = (String) -> ()
    class StringExpecterHolder : NSObject {
        var f : MyStringExpecter! = nil
    }
    

    Here's how to use it to wrap a function and pass where an AnyObject is expected:

    func f (s:String) {println(s)}
    let holder = StringExpecterHolder()
    holder.f = f
    
    let lay = CALayer()
    lay.setValue(holder, forKey:"myFunction")
    

    And here's how to extract the function later and call it:

    let holder2 = lay.valueForKey("myFunction") as StringExpecterHolder
    holder2.f("testing")
    
    0 讨论(0)
  • 2020-12-05 16:00

    All you need to do is use reinterpretCast to perform force cast.

    (reinterpretCast(completion) as (@objc_block Void -> Void))()
    

    from 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>  
    
    0 讨论(0)
提交回复
热议问题