Skip to content

Instantly share code, notes, and snippets.

@victor-pavlychko
Created September 7, 2020 14:42
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save victor-pavlychko/8a896917d8c73f4dded594ab4782214e to your computer and use it in GitHub Desktop.
Save victor-pavlychko/8a896917d8c73f4dded594ab4782214e to your computer and use it in GitHub Desktop.
import Foundation
// MARK: - Protocols
@objc protocol NSMethodSignaturePrivate {
static func signature(objCTypes: UnsafePointer<CChar>) -> NSMethodSignaturePrivate?
var numberOfArguments: Int { get }
@objc(getArgumentTypeAtIndex:) func getArgumentType(at index: Int) -> UnsafePointer<CChar>
var frameLength: Int { get }
var isOneway: ObjCBool { get }
var methodReturnType: UnsafePointer<CChar> { get }
var methodReturnLength: Int { get }
}
@objc protocol NSInvocationPrivate {
static func invocation(methodSignature: NSMethodSignaturePrivate) -> NSInvocationPrivate
var methodSignature: NSMethodSignaturePrivate { get }
func retainArguments()
var argumentsRetained: ObjCBool { get }
var target: AnyObject { get set }
var selector: Selector { get set }
func getReturnValue(_ location: UnsafeMutableRawPointer)
func setReturnValue(_ location: UnsafeRawPointer)
@objc(getArgument:atIndex:) func getArgument(_ location: UnsafeMutableRawPointer, at index: Int)
@objc(setArgument:atIndex:) func setArgument(_ location: UnsafeRawPointer, at index: Int)
func invoke()
func invoke(target: AnyObject)
}
@objc protocol NSObjectPrivate {
@objc(methodSignatureForSelector:) func methodSignature(for selector: Selector) -> NSMethodSignaturePrivate?
func forwardInvocation(_ invocation: NSInvocationPrivate)
}
// MARK: - Import class helper
func importClass<ProtocolType>(_ className: String) -> ProtocolType {
let typeNameSuffix = ".Type"
let protocolTypeName = String(reflecting: ProtocolType.self)
guard protocolTypeName.hasSuffix(typeNameSuffix) else {
preconditionFailure("Type `\(protocolTypeName)` is not a protocol type.")
}
let protocolName = protocolTypeName.dropLast(typeNameSuffix.count)
guard let `protocol` = protocolName.withCString(objc_getProtocol) else {
preconditionFailure("Type `\(protocolName)` is not an objc protocol.")
}
guard let `class` = className.withCString(objc_getClass) as? AnyClass else {
preconditionFailure("Class `\(className)` not found.")
}
if !class_addProtocol(`class`, `protocol`) {
assertionFailure("Failed to attach protocol `\(protocolName)` to class `\(className)`.")
}
guard let result = `class` as? ProtocolType else {
fatalError("Failed to cast class `\(className)` to protocol `\(protocolName)`.")
}
return result
}
// MARK: - Import classes
let NSMethodSignatureClass = importClass("NSMethodSignature") as NSMethodSignaturePrivate.Type
let NSInvocationClass = importClass("NSInvocation") as NSInvocationPrivate.Type
let NSObjectClass = importClass("NSObject") as NSObjectPrivate.Type
// MARK: - Try out the NSInvocation
let object = NSDate()
let objectPrivate = object as! NSObjectPrivate
let selector = Selector("description")
let signature = objectPrivate.methodSignature(for: selector)!
let invocation = NSInvocationClass.invocation(methodSignature: signature)
invocation.selector = selector
invocation.invoke(target: object)
var unmanagedResult: Unmanaged<NSString>? = nil
invocation.getReturnValue(&unmanagedResult)
let result = unmanagedResult?.takeRetainedValue()
print(result ?? "<nil>")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment