I'm trying to work out a way to call methods dynamically by number. Here's a simplified version of what I'm doing.
class C {
func a() {}
func b() {}
let f = [0: a, 1: b]
func call(n: Int) {
f[n]?()
}
}
let c = C()
c.call(0)
When I run this in a playground I get
Playground execution failed: error: <REPL>:10:13: error: could not find an overload for 'subscript' that accepts the supplied arguments
f[n]?()
~~~~^~~
Yet if I run
func a() {}
func b() {}
let f = [0: a, 1: b]
f[0]?()
directly with no containing class it works as expected. What's going on?
This is really interesting! I noticed that your first bit of code ran fine if I moved the function definitions outside of the class but kept everything else the same. From the error message I was able to conclude that when the functions are declared within the class they need to be called with an instance of the class. When you just call a() inside the class the compiler is automatically interpreting that as self.a(). However, when the function is stored in a variable (f[0], f[1], etc.) it needs to be passed an instance of class C (self) first. This works:
class C {
func a() {println("in a")}
func b() {println("in b")}
let f = [0: a, 1: b]
func call(n: Int) {
a() // works because it's auto-translated to self.a()
f[n]?(self)() // works because self is passed in manually
}
}
let c = C()
c.call(0)
Related
I have experience in android dev mostly. I am trying to expand into Swift and IOS. I think I understand why below would print out "B" but why would you ever use "let a: A = B()"? This just could be just simple screening question but I just couldn't come up with a reason to code this way. Help me out brothers!
class A{
func printMsg(){
print("A")
}
}
class B: A{
override func printMsg() {
print("B")
}
}
let a: A = B()
a.printMsg()
Here in let a: A = B(), constant property a is of type class A and we are assigning subclass B to it. We can only call overridden functions by this way, but if you add some other functions in subclass B, it wont be accessible. So this will call the overridden function and print "B". If you do let a: A = A(), it will call the printMsg in class A and print "A". Also there is nothing wrong to write let a: B = B() and then print "B" like a.printMsg(). Both are the same.
You asked:
but why would you ever use let a: A = B()?
In practice, you wouldn’t see that very often. They’re just testing your ability to understand the difference between the variable’s type, A, from the instance’s type, B. A far more common, practical, real-world example would be a function that takes an instance of A as a parameter:
func foo(a: A) {
a.printMsg()
}
So you have some function that will take any A type.
So, you could do things like:
let b = B()
foo(a: b) // will print “B”
But, obviously, you could still do:
let a = A()
foo(a: a) // will print “A”
The interview question of let a: A = B() is just a simplified rendition of the above. The fact that we’re using a func isn’t really relevant (though it’s the practical example). The interview question is focusing on the deeper question, namely your understanding of the distinction between the variable’s type and the instance’s type, specifically how it affects the determination as to which method implementation is called.
FWIW, the typical, advanced, follow-up to this particular interview question is often the protocol default implementation rendition, namely:
protocol C { }
extension C {
func printMsg() {
print("C")
}
}
class D: C {
func printMsg() {
print("D")
}
}
let c: C = D()
c.printMsg() // will print “C”, not “D”!!!
As you’ll see, the behavior is different. If you want to get the same dynamic dispatch behavior with protocol types, the method in question has to be part of the protocol definition:
protocol C {
func printMsg()
}
// the rest is the same
Now, you’ll get “D”:
let c: C = D()
c.printMsg() // will print “D”
I came across the following code in SR-142 on bugs.swift.org.
If a protocol has an extension method that's mutating, a class instance can call the mutating function without any problem.
// protocol definition
protocol P { }
extension P {
mutating func m() { }
}
// class conforming to P
class C : P {
// redeclare m() without the mutating qualifier
func m() {
// call protocol's default implementation
var p: P = self
p.m()
}
}
let c = C()
c.m()
If I make a small change to add the method to the protocol declaration:
protocol P {
mutating func m() // This is what I added.
}
extension P {
mutating func m() { }
}
class C : P {
func m() {
var p: P = self
p.m()
}
}
let c = C()
c.m() // This one is calling itself indefinitely; why?
Why does c.m() keep calling itself again and again?
With your change in the second example, by including the m in the protocol definition, that instructs Swift to employ dynamic dispatch. So when you call p.m(), it dynamically determines whether the object has overridden the default implementation of the method. In this particular example, that results in the method recursively calling itself.
But in the first example, in the absence of the method being part of the protocol definition, Swift will employ static dispatch, and because p is of type P, it will call the m implementation in P.
By way of example, consider where the method is not part of the protocol definition (and therefore not in the “protocol witness table”):
protocol P {
// func method()
}
extension P {
func method() {
print("Protocol default implementation")
}
}
struct Foo: P {
func method() {
print(“Foo implementation")
}
}
Because the foo is a P reference and because method is not part of the P definition, it excludes method from the protocol witness table and employs static dispatch. As a result the following will print “Protocol default implementation”:
let foo: P = Foo()
foo.method() // Protocol default implementation
But if you change the protocol to explicitly include this method, leaving everything else the same, method will be included in the protocol witness table:
protocol P {
func method()
}
Then the following will now print “Foo implementation”, because although the foo variable is of type P, it will dynamically determine whether the underlying type, Foo, has overridden that method:
let foo: P = Foo()
foo.method() // Foo implementation
For more information on dynamic vs static dispatch, see WWDC 2016 video Understanding Swift Performance.
By declaring m in the protocol & providing implementation in your class, it over writes the default implementation.
But in the first example when you cast your class as protocol it will call protocol's default implementation because the implementation of the class is it own and not over writing any of the protocol's method
Suppose I have a class class C<T>.
I want to write a function f(_ a: Any) -> Bool, which returns true when a is a member of class inheriting from C (or is a C itself). I don't care about the specialization: passing C<Int>, C<Double>, C<Whatever> should all return true.
It seems like I should be able to just write a is C or a as? C != nil as the body of that function, however, these both don't compile in a playground; swiftc complains that "generic parameter 'T' could not be inferred in cast to 'C<_>'". If I write f inside of C as an instance method, C is implicitly C<T>, and so writing let c = C<Int>(); c.f(C<Double>()) returns false.
I can work around this by writing a protocol P, which C conforms to, and then test for that but I don't consider that a good solution; it's just a hack.
Is there any way to do this?
Here's all the code I wrote to try this:
class C<T> {
func test(_ a: Any) -> (Bool, Bool) {
return (type(of: a) == C.self, a is C)
}
}
class D: C<Double> { }
let d = D()
func f(_ a: Any) -> Bool {
return a is C // generic parameter 'T' could not be inferred in cast to 'C<_>'
}
d.test(C<Int>()) // false, false
// bad solution
protocol P { }
extension C: P { }
d is P // true
I don't think a language feature exists for you to do this, because it's not useful to just know that an object is a subclass of, or C itself, without knowing the generic type parameters.
I mean, what are you gonna do after knowing that, yes, someObj is indeed of type C<Something>, but not what Something is? You can't do anything with someObject. You can't cast it so you can't access any members of it.
Swift's generics is very very strict, unlike Java, who just throws generic types out of the window at runtime...
If you insist that you want to do this, your only option is to use the hack you mentioned.
Or, if you just want to check whether a is C itself, not any of its subclasses, you can use this (just for fun):
func f(a: Any) -> Bool {
let regex = try! NSRegularExpression(pattern: "C<.+>")
let typeString = String(describing: type(of: a))
let range = NSRange(location: 0, length: typeString.characters.count)
let matchRange = regex.rangeOfFirstMatch(in: typeString, range: range)
return range.toRange() == matchRange.toRange()
}
Writing a protocol that specifies what you are expecting for this test is not at all a hack; it's exactly how Swift should be used. (What you are asking is indeed impossible, and purposefully so.) Write that protocol, but not as an empty hack; fill it with what you are testing for and then view it as a beautiful part of your code's structure!
See how dump is declared
func dump<T, TargetStream where TargetStream : TextOutputStream>(_ value: T, to target: inout TargetStream, name: String? = default, indent: Int = default, maxDepth: Int = default, maxItems: Int = default) -> T
And check what it produces
class C<T>{}
class D:C<Double>{}
let c = C<Void>()
let d = D()
dump(c)
dump(d)
Yes, dump is part of Swift's standard library ... which use reflection (objects mirror) to produce its result.
UPDATE
I created cli project named dumptest with one file main.swift
//
// main.swift
// dumptest
//
class C<T>{}
class D:C<Double>{}
class E:D{}
let c = C<Void>()
let d = D()
let e = E()
var tc = ""
var td = ""
var te = ""
dump(c, to: &tc)
dump(d, to: &td)
dump(e, to: &te)
print("type info about c:", tc)
print("type info about d:", td)
print("type info about e:", te)
running it the program reported
type info about c: - dumptest.C<()> #0
type info about d: - dumptest.D #0
- super: dumptest.C<Swift.Double>
type info about e: - dumptest.E #0
- super: dumptest.D
- super: dumptest.C<Swift.Double>
Program ended with exit code: 0
For parsing String variables check stackoverflow, or ask a new question ...
lets have
import Cocoa
let v = NSView()
dump(v)
dump value v and we have
- <NSView: 0x100a022a0> #0
- super: NSResponder
- super: NSObject
Program ended with exit code: 0
If you need something "more complex", you can play with
Mirror
I have a Swift program that produces a compiler error I cannot understand. It boils down to the following code fragment.
Class Ais a helper class that is upon initialization provided with a handler function that is to be called later. This function has an A object as an argument.
Class B initializes an object of type A and has a function f that should be used as the handler. Alternatively, an anonymous code block (closure) could be provided.
class A {
init(_: (A) -> Void) {}
}
class B {
let a = A(f)
func f(a: A) {}
}
This produces the following compiler message:
error: cannot convert value of type 'B -> (A) -> ()' to expected argument type '(A) -> Void'
let a = A(f)
^
Void and () are equivalent, I suppose.
I do not even understand the type expression with the double ->. What kind of type is this?
Can anyone explain what's wrong here? And, how to do it right?
You can't refer to an instance method (like f) while initializing an instance property (like a), because the instance is exactly what we are in the middle of creating. One solution is to declare your instance property lazy, like this:
class A {
init(_: (A) -> Void) {}
}
class B {
func f(a: A) {}
lazy var a : A = A(self.f)
}
That is legal because lazy guarantees that the initializer for a won't be evaluated until later, when the instance has been created. Note that self is absolutely required in this context (and in general I recommend you always use it wherever it can be used).
Another solution is to declare a as an A! (so that it has an initial value, namely nil) and initialize it "for real" later. For example:
class A {
init(_: (A) -> Void) {}
}
class B {
func f(a: A) {}
var a : A!
init() {
self.a = A(self.f)
}
}
#matt pointed me the right direction — thank you very much. His solution is the a1 variant here:
class B {
func f(a: A) {}
lazy var a1 : A = A(self.f) // ok
lazy var a2 : A = A(f) // error
lazy var a3 = A(self.f) // error
lazy var a4 = A(f) // error
}
Interestingly, all other variants a2, a3, a4 do not work. a2 and a4 produce the original "cannot convert value" error, a3 says "use of unresolved identifier 'self'".
The disadvantage of the lazy solution is that a is now a var instead of a constant. I have found an alternative way by assigning the constant value during initialization:
class B {
let a: A
init() {
a = A() { (A) -> Void in … } // ok
}
}
Interestingly, this only works with a closure. The code
class B {
let a: A
init() {
a = A(f) // error
}
func f(a: A) { … }
}
gives the compiler error "use of 'self' in method call 'f' before all stored properties are initialized".
Nevertheless, even in the closure variant, self cannot be used since it is not yet fully initialized.
Please consider the following classes:
// Models:
class A {}
class B: A { }
// Parsers:
class AbstractParser<T> {}
class ParserB<T: B>: AbstractParser<T> {}
// Services:
class AbstractService<T> {
func parser() -> AbstractParser<T> {
fatalError("This method must be overridden")
}
}
class ServiceA<T: A>: AbstractService<T> {
}
class ServiceB<T: B>: ServiceA<T> {
private let _parser = ParserB()
override func parser() -> ParserB<B> {
return _parser
}
}
I'm getting an error Method doesn not override any method from it's superclasses at overriden parser function. I could easily fix this by changing
class ServiceB<T: B>: ServiceA<T>
to
class ServiceB<T: B>: ServiceA<B>
but this will break a solution from this question: A variable in generic class gets wrong type
Is there any workaround for this?
EDIT
Thanks, Kenneth Bruno, your approach works, but it again leads to another error with types.
I add class C:
class C {
var item = B()
}
and a simple method to ServiceB:
func doSomething() {
var entities = [T]()
let c = C()
entities.append(c.item)
}
This causes error: Cannot invoke 'append' method with an argument list of type '(B)'. It seems the compiler can't understand that B and T are the same thing?
Also please note that I can't define var entities = [B](), as I need to pass this array to another function in AbstractService method.
Just as in your other question you need to use the generic type instead of a specific type, then the method signatures will match to override the function.
class ServiceB<T: B>: ServiceA<T> {
private let _parser = ParserB<T>()
override func parser() -> ParserB<T> {
return _parser
}
}
From the question edit:
This causes error: Cannot invoke 'append' method with an argument list of type '(B)'. It seems the compiler can't understand that B and T are the same thing?
Just to clarify things. In the edit code example <T: B> and B are not the same thing. B is a regular type, while <T: B> is a generic type, which may represent a B type or any of it's subtypes.
Merging the question code with the code proposed by #Kenneth results in the following, which leads to a type error
class C {
var item = B()
}
class ServiceB<T: B>: ServiceA<T> {
private let _parser = ParserB<T>()
override func parser() -> ParserB<T> {
return _parser
}
func doSomething() {
var entities = [T]()
let c = C()
entities.append(c.item) // Error: Cannot invoke 'append' method with an argument list of type '(B)'
}
}
Now let's say in the future we add a new type D, subtype of B and instantiate a ServiceB<D>. This would cause the function doSomething() to try to append an instance of B in an array of D which is illegal, that's why the compiler raises an error.
With the code proposed in the comments by #Kenneth, the entities array would be filled in the ServiceB<B> case, but would always be empty in the ServiceB<D>.
class D: B { }
class ServiceB<T: B>: ServiceA<T> {
...
func doSomething() {
var entities = [T]()
let c = C()
if let item = c.item as? T { entities.append(item) }
}
}
let service = ServiceB<B>()
service.doSomething() // Creates an array of B and append a single B instance on it
let serviceD = ServiceB<D>()
serviceD.doSomething() // Creates an array of D, c.item of type B can't be cast to D, the array will be empty
While my answer doesn't really solves your problem, I think it should put you one step closer to a solution.