Swift closure as AnyObject
Asked Answered
L

7

11

Im trying to use this method: class_addMethod() which in Obj-c is used like this:

class_addMethod([self class], @selector(eventHandler), imp_implementationWithBlock(handler), "v@:");

And Im using it like this in Swift:

class_addMethod(NSClassFromString("UIBarButtonItem"), "handler", imp_implementationWithBlock(handler), "v@:")

It is an extension for UIBarButtonItem as you might have figured out.

imp_implementationWithBlock takes a parameter of type AnyObject!

How can I cast ()->() into AnyObject ?

I've tried to cast it like this: handler as AnyObject but it gives me an error saying: ()->() does not conform to protocol 'AnyObject'

Legman answered 29/1, 2015 at 10:11 Comment(1)
Did you try using reinterpretCast()?Omura
P
11

How can I cast ()->() into AnyObject ?

Warning: This answer includes undocumented and unsafe feature in Swift. I doubt this passes AppStore review.

let f: ()->() = {
    println("test")
}

let imp = imp_implementationWithBlock(
    unsafeBitCast(
        f as @objc_block ()->(),
        AnyObject.self
    )
)
Piecework answered 29/1, 2015 at 10:49 Comment(7)
What is 'f' ? Is it the closure?Legman
So why has Apple a func imp_implementationWithBlock(_ block: AnyObject!) -> IMP in the lib reference if you need that code you posted? Bug?Aggrieve
Obj-C code can passes Blocks to Swift AnyObject type parameter.Piecework
If you want to add this as an extension you are free to use it: extension UIBarButtonItem { convenience init(title: String?, style: UIBarButtonItemStyle, handler: ()->()) { let imp = imp_implementationWithBlock(unsafeBitCast(handler as @objc_block ()->(), AnyObject.self)) class_addMethod(UIBarButtonItem.self, "handler", imp, "v@:") self.init(title: title, style: style, target: nil, action: "handler") self.target = self } }Legman
I'm not sure this extension does work as you expected. As far as I know, Method is class wide property, not instance local. So I think this results every instance shares same handler, no?Piecework
Hmm maybe the method will be created for every but only the ones I add the handler to will try to call it. But I will test it further.Legman
Should use @convention instead of @objc_blockEyeglass
F
8

You could write a wrapper, then pass it to the function

class ObjectWrapper<T> {
    let value :T
    init(value:T) {
       self.value = value
    }
}

let action = ObjectWarpper(value: {()->() in    
    // something
})
Facelifting answered 8/9, 2015 at 9:55 Comment(0)
E
6

In Swift 2, you should use @convention instead of @objc_block. See Type Attribute

func swizzle(type: AnyClass, original: Selector, methodType: MethodType, block: () -> Void) {
    let originalMethod = method(type, original: original, methodType: methodType)

    let castedBlock: AnyObject = unsafeBitCast(block as @convention(block) () -> Void, AnyObject.self)

    let swizzledImplementation = imp_implementationWithBlock(castedBlock)
    // More code goes here
}
Eyeglass answered 16/1, 2016 at 9:54 Comment(0)
A
4

You can't. You can only cast it to Any.

  • AnyObject can represent an instance of any class type.
  • Any can represent an instance of any type at all, including function types.

Apple Inc. „The Swift Programming Language.“ iBooks. https://itun.es/de/jEUH0.l

Aggrieve answered 29/1, 2015 at 10:34 Comment(5)
But is there another way of doing this in any way?Legman
Have you tried to pass Selector("handler")? Can you show the signature of imp_implementationWithBlock ?Aggrieve
The error isnt the selector part, its the imp_implementationWithBlock() that is causing confusonLegman
imp_implementationWithBlock(block: AnyObject!) takes only an AnyObject as parameter and returns an IMPLegman
Its found in Objective-C Runtime referencesLegman
G
2

This has worked for me:

let myBlock: @objc_block () -> Void = { 
}
var mf : AnyObject = unsafeBitCast(myBlock, AnyObject.self)
Glucoside answered 15/8, 2015 at 0:31 Comment(0)
D
1

In Swift 4.x (I think works in 3.x too), simply declaring the closure as @convention(block) should be enough to address compatibility thing:

// define the new implementation
let handler: @convention(block) (UIBarButtonItem) -> Void = { _ in }

// inject it into the class
class_addMethod(NSClassFromString("UIBarButtonItem"), sel_registerName("handler"), imp_implementationWithBlock(handler), "v@:")

Though, starting with Swift 3, AnyObject was converted to Any if the reference comes from Objective-C, so the code will compile even without the @convention(block) part, however it will crash at runtime as the compiler will not convert the Swift closure to an Objective-C block.

Disciplinarian answered 28/9, 2018 at 6:36 Comment(0)
J
0

Use Any object (The protocol to which all types implicitly conform)

 let aBlock: (view: View) -> Void = { view in /**/ }

 let block:Any? = aBlock
Jubilate answered 27/4, 2016 at 11:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.