Type Encoding for an IMP

Is there a way in swift to calculate this type encoding string? The user case is to create an IMP using imp_implementationWithBlock and add it to a NSObject at runtime using class_addMethod. I know an alternative would be to declare the implementation using an @objc func and get the type using method_getTypeEncoding but a wonder if this is a way to use just the IMP from a block without have to manually construct the type encoding string.

Hi Mateus! Swift does not include a mechanism to derive an Objective-C type encoding string. The use of such strings is generally limited to the far corners of Objective-C. If you need such a string, you can get it by creating an @objc method with the right signature, then asking the runtime about it (i.e. use method_getTypeEncoding() as you suggested.)

imp_implementationWithBlock() has some surprising performance implications—in particular that it requires mapping in one or more executable pages from disk when used. Are you sure you can't just declare an @objc func that does what you want? Can you explain in more detail why that won't work for you?

If all else fails and you really do need to use class_addMethod(), don't fret. Fortunately, type encoding strings aren't actually used very much these days, so you can often simply pass the empty string and everything will "just work."

1 Like

Thanks for answering! I actually have no particular use case that I need to use an IMP block directly and I agree that using a method with @objc is usually a better solution.
I was intrigued by this because of the library InterposeKit that swizzles object methods using implementation blocks. There is no such problem with the type encoding in the library because it works only to replace methods already implemented so it is easy to extract type encoding from the original implementation.
Then I found a case that I needed to actually add a new method and found this problem with type encoding for IMP blocks doing this without InterposeKit.

In fact, I added an implementation using class_addMethod and passing nil to the types parameter and it just worked but it felt suspicious and something wrong to do that could break.

Unless you're using some really arcane stuff like Distributed Objects (from the early years of Mac OS X), or if you're setting up an NSXPCInterface, you're probably not going to need type encodings for methods. So nil or the empty string is fine.

If you really want a valid type string, get it from another method:

@objc private class _MyTypeEncodings: NSObject {
    @objc(correspondingSelector:someInt:someString:)
    fileprivate func correspondingSelector(_: Selector, _: Int, _: String) -> NSAppleScript {
        fatalError("Unimplemented")
    }
}

let typeEncoding: String? = {
    guard let method = class_getInstanceMethod(_MyTypeEncodings.self, #selector(_MyTypeEncodings.correspondingSelector(_:_:_:))) else {
        return nil
    }
    guard let typeEncoding = method_getTypeEncoding(method) else {
        return nil
    }
    return String(cString: typeEncoding)
}()
1 Like