Parameterized Initializer Reflection in Swift - swift

I am trying to apply reflection on a swift class (someClass) to invoke an init method that takes one argument (someArg), I managed to get the init selector and IMP that has 1 argument, but when I invoke the IMP it ends up calling the init with no arguments. In the Playground below I always get "called the wrong init" printed.
If I remove the override init I get the following error:
fatal error: use of unimplemented initializer 'init()' for class '__lldb_expr_15.someClass'
What am I missing?
import UIKit
public class someClass:NSObject{
init( num:someArg){
print("called the right init")
}
override init(){
print("called the wrong init")
}
}
public class someArg:NSObject{
override init(){
}
}
public class Test{
func reflect(){
let classType: NSObject.Type = someClass.self as NSObject.Type
let (initializerWithOneArgImp,selector) = getInitializerWithArguments(classType, argumentsCount: 1)
typealias initializerWithOneArgImpType = #convention(c) (AnyObject, Selector, AnyObject) -> (AnyObject)
let callback = unsafeBitCast(initializerWithOneArgImp , initializerWithOneArgImpType.self)
callback(classType,selector,someArg())
}
func getInitializerWithArguments(classType:AnyClass, argumentsCount:Int)->(IMP,Selector){
var methodCount:CUnsignedInt = 0
let methodList = class_copyMethodList(classType.self, &methodCount)
let n : Int = Int(methodCount)
for var i: Int = 0; i < n; i++ {
let methodSelector = method_getName(methodList[i])
let methodName:String = String(_sel:methodSelector)
if(methodName == "init")
{
let methodArgumentsCount = method_getNumberOfArguments(methodList[i])
if(methodArgumentsCount == UInt32(argumentsCount) + 1)
{
return (method_getImplementation(methodList[i]),methodSelector)
}
}
}
return (nil,nil)
}
}
var test = Test()
test.reflect()

Turns out, the non parametrized init has two arguments by default, and the parameterized init would have "initWithNum" as methodName.
if(methodName.hasPrefix("init"))
{
let methodArgumentsCount = method_getNumberOfArguments(methodList[i])
if(methodArgumentsCount == UInt32(argumentsCount) + 2)
{
return (method_getImplementation(methodList[i]),methodSelector)
}
}

Related

Is it possible to call generic object constructor using reflection in swift?

let's say we have a class
class Test: NSObject {
let batman: String
let spiderman: Int
let superman: NSObject
init(batman: String, spiderman: Int, superman: NSObject) {
self.batman = batman
self.spiderman = spiderman
self.superman = superman
}
}
And a generic method for initialization:
func resolve<T: NSObject>(args: Any...) throws -> T {
// if let object = initWithReflectionSomeHow(T.className, arg1, arg2...) {
// return object
// } else {
// throw exception
// }
}
I found a way to to this without parameters like this:
func resolve<T: NSObject>() throws -> T {
if let objectClass = NSClassFromString(T.className) as? T.Type {
let object = objectClass.init()
return object
} else {
//throw classNotFoundException
}
}
So I would just call:
let obj = try resolve() as Test
but not sure how I can inject parameters. Is it even possible?

Problem with swift polymorphism and dynamic type

I just encountered a strange behavior in swift's inheritance handling, when it comes to polymorphism and dynamic types. The following code shows the problem I encounter, which basically is: The dynamic type is recognized correctly (printed by print("type(of: self) = \(classType)")), but the generic function testGeneric uses the wrong type.
class Global {
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
}
class TestSuperClass {
func run() {
let classType = type(of: self)
print("type(of: self) = \(classType)")
Global.testGeneric(of: classType)
}
}
class TestClass: TestSuperClass {
}
class TestClass2: TestSuperClass {
override func run() {
let classType = type(of: self)
print("type(of: self) = \(classType)")
Global.testGeneric(of: classType)
}
}
let testClass = TestClass()
let testClass2 = TestClass2()
testClass.run()
testClass2.run()
the printed output is
type(of: self) = TestClass
T.Type = TestSuperClass
type(of: self) = TestClass2
T.Type = TestClass2
So basically when calling testClass.run(), type(of: self) yields TestClass, which I would expect. The problem then is that the generic function testGeneric, which is called immediately afterwards, somehow does not work with type TestClass, but uses TestSuperClass instead.
What I personally would expect is
type(of: self) = TestClass
T.Type = TestClass
type(of: self) = TestClass2
T.Type = TestClass2
i.e., the generic function testGeneric using the type TestClass instead of TestSuperClass when called via testClass.run().
Questions:
- Do you have an explanation for that?
- How can I obtain the behavior I had in mind?
In Swift, the compiler want's to know at compile time which generic type to "infer". Therefore, the type system will bind to the static type. There is no such thing as dynamic type inference.
Therefore the compiler generates the following (see comments):
class TestSuperClass {
func run() {
let classType = type(of: self) // static MetaType TestSuperClass.Type
print("type(of: self) = \(classType)") // dynamic type: TestClass
Global.testGeneric(of: classType) // infer to static type, i.e. testGeneric<TestSuperClass>
}
}
As a result, T.self is TestSuperClass in your case, because that's what the compiler is able to see:
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
What you maybe want is the following:
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(type)")
}
Here, you do not print the type of T, but the (dynamic) value of the parameter type, which in your case is TestClass
To answer the second question: You will not be able to change the dynamic type of the retured array; it will always be [TestSuperClass] - although it will contain TestClass objects:
class Global {
static func testGeneric<T: TestSuperClass>(of type: T.Type) {
print("T.Type = \(T.self)")
}
static func returnObjects<T: TestSuperClass>(of theType: T.Type) -> [T] {
let newObj = theType.init()
let newObjType = type(of:newObj)
print("type(of: newObj) = \(newObjType)")
return [newObj]
}
}
class TestSuperClass {
required init() {
print ("TestSuperClass.init")
}
func run() {
let classType = type(of: self)
print("type(of: self) = \(classType)")
Global.testGeneric(of: classType)
let array = Global.returnObjects(of: classType)
let arrayType = type(of:array)
print("type(of: self) = \(arrayType)")
print (array)
}
}
class TestClass: TestSuperClass {
required init() {
super.init()
print("TestClass.init")
}
}
let testClass = TestClass()
testClass.run()
TestSuperClass.init
TestClass.init
type(of: self) = TestClass
T.Type = TestSuperClass
TestSuperClass.init
TestClass.init
type(of: newObj) = TestClass
type(of: self) = Array < TestSuperClass >
[__lldb_expr_21.TestClass]

swift: Assign function type to variable

struct System {
var method: (() -> ())?
var curMethod: Int
init() {
method = nil
curMethod = 0
}
mutating func method1() {
curMethod = 1
}
mutating func method2() {
curMethod = 2
}
}
var sys = System()
sys.method = System.method1
sys.method!()
I get an error cannot assign value of type (inout System) -> () -> ()' to type '(() -> ())?. What am I doing wrong?
First of all, your line sys.method = System.method1 is wrong, as it would require method1 to be a static function (like a class function) rather than an instance function, so it should be changed to sys.method = sys.method1. But this isn't allowed - the error is "error: partial application of 'mutating' method is not allowed".
If you make System a class (rather than a struct), it will work if you replace the System.method1 with sys.method1.
The reason for this is that a mutating func is actually quite a lot more than a simple function under the hood - it is a curried function (curried with a compiler generated function) that effectively creates a new copy of the struct with the "new" value - hence, you A) can't access it it directly ("partial application is not allowed") and B) you can't assign it to a ()->() variable.
So, there're 3 variants suggested by participants. Everything is working, and using class instead of struct seems to me less complicated.
struct System1 {
var method: (() -> ())?
var curMethod: Int
init() {
method = nil
curMethod = 0
}
mutating func method1() { curMethod = 1 }
mutating func method2() { curMethod = 2 }
}
struct System2 {
var method: ((inout System2) -> ())?
var curMethod: Int
init() {
method = nil
curMethod = 0
}
mutating func callCurrentMethod() { method?(&self) }
mutating func method1() { curMethod = 1 }
mutating func method2() { curMethod = 2 }
}
class System3 {
var method: (() -> ())?
var curMethod: Int
init() {
method = nil
curMethod = 0
}
func method1() { curMethod = 1 }
func method2() { curMethod = 2 }
}
var struct1 = System1()
var struct2 = System2()
var class1 = System3()
print(struct1.curMethod)
let curried = System1.method1
let unsafe = curried(&struct1)
unsafe()
print(struct1.curMethod)
print(struct2.curMethod)
struct2.method = { $0.method1() }
struct2.callCurrentMethod()
print(struct2.curMethod)
print(class1.curMethod)
class1.method = class1.method1
class1.method!()
print(class1.curMethod)

Blank constant when trying to get list of classes that have adopted a Protocol

I am trying to get a list of classes that have adopted a certain Protocol Migration: Preparation, and then to append those classes into an array. Here is the function in question:
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
var migrationsList = [Preparation.Type]()
var count = UInt32(0)
let classList = objc_copyClassList(&count)!
for i in 0..<Int(count) {
let classInfo = ClassInfo(classList[i])!
if let cls = classInfo.classObject as? Migration.Type {
migrationsList.append(cls)
print(cls.description)
}
}
return migrationsList
}
}
In principle all that should work, but when debugging I note that the classInfo variable is referring to each class in the iteration, but when assigning and casting in the if let as line, the constant cls is always blank - neither a value/class nor nil, just completely blank.
Any idea what I got wrong with that code?
I am also open to suggestions for any better way to get a list of all classes that have adopted a particular protocol...
EDIT: I forgot to provide the code for ClassInfo
import Foundation
struct ClassInfo: CustomStringConvertible, Equatable {
let classObject: AnyClass
let className: String
init?(_ classObject: AnyClass?) {
guard classObject != nil else { return nil }
self.classObject = classObject!
let cName = class_getName(classObject)!
self.className = String(cString: cName)
}
var superclassInfo: ClassInfo? {
let superclassObject: AnyClass? = class_getSuperclass(self.classObject)
return ClassInfo(superclassObject)
}
var description: String {
return self.className
}
static func ==(lhs: ClassInfo, rhs: ClassInfo) -> Bool {
return lhs.className == rhs.className
}
}
I can't explain why cls is always blank, like I said in my comment it's something I run into every time I'm dealing with meta types. As for making the code work as intended, I found this q&a and updated it with Swift 3 to get this code which should cover your situation. It's important to stress that this will only work if you correctly expose Swift to the Objective-C runtime.
Drop this code anywhere and call print(Migrations.getMigrations()) from a convenient entry point.
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
return getClassesImplementingProtocol(p: Preparation.self) as! [Preparation.Type]
}
static func getClassesImplementingProtocol(p: Protocol) -> [AnyClass] {
let classes = objc_getClassList()
var ret = [AnyClass]()
for cls in classes {
if class_conformsToProtocol(cls, p) {
ret.append(cls)
}
}
return ret
}
static func objc_getClassList() -> [AnyClass] {
let expectedClassCount = ObjectiveC.objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = ObjectiveC.objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyClass]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)] {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
class Migration: Preparation {
}
#objc
protocol Preparation {
}

Opposite of __conversion in Swift to assign to a value of a different type

Swift provides a special method called __conversion that allows you to implicitly convert your type to another type. I would like to be able to define a method that allows you to go the other way: to be able to assign a custom type to another type and have it implicitly converted to allow the assignment to work.
Forward usage that is valid:
class MyClass<T> {
var myValue : T
func __conversion() -> T? {
return myValue
}
init(value: T) {
self.myValue = value
}
}
func takesString(aString: String?) {
}
var myInstance = MyClass(value: "Hello")
takesString(myInstance)
What I would like to do:
class MyClass<T> {
func __conversion(aValue: T) -> MyClass<T> {
return MyClass(value: T)
}
}
var myInstance : MyClass<String> = "Hello World" // compiler error
Is there any way to do this?
It turns out that __conversion is a private method and will be removed by the end of the beta so this will definitely not be possible once Swift is released.
Have you tried adding an extension to String?
extension String {
func __conversion() -> MyClass {
var myInstance = MyClass()
myInstance.myString = self
return myInstance
}
}
It worked in an iOS playground in Xcode 6 Beta 4.
let anInstance: MyClass = "test"
To deal with generics:
class MyClass<T> {
var myString = ""
var myT : T?
}
extension String {
func __conversion<T>() -> MyClass<T> {
var myInstance = MyClass<T>()
myInstance.myString = self
return myInstance
}
}
let anInstance: MyClass<Int> = "test"
BTW: I think the Apple approved answer is an init method.
let anInstance = MyClass(myString: "test")