I tend to only put the necessities (stored properties, initializers) into my class definitions and move everything else into their own extension
, kind of like an extension
per logical block that I would group with // MARK:
as well.
For a UIView subclass for example, I would end up with an extension for layout-related stuff, one for subscribing and handling events and so forth. In these extensions, I inevitably have to override some UIKit methods, e.g. layoutSubviews
. I never noticed any issues with this approach -- until today.
Take this class hierarchy for example:
public class C: NSObject {
public func method() { print("C") }
}
public class B: C {
}
extension B {
override public func method() { print("B") }
}
public class A: B {
}
extension A {
override public func method() { print("A") }
}
(A() as A).method()
(A() as B).method()
(A() as C).method()
The output is A B C
. That makes little sense to me. I read about Protocol Extensions being statically dispatched, but this ain't a protocol. This is a regular class, and I expect method calls to be dynamically dispatched at runtime. Clearly the call on C
should at least be dynamically dispatched and produce C
?
If I remove the inheritance from NSObject
and make C
a root class, the compiler complains saying declarations in extensions cannot override yet
, which I read about already. But how does having NSObject
as a root class change things?
Moving both overrides into their class declaration produces A A A
as expected, moving only B
's produces A B B
, moving only A
's produces C B C
, the last of which makes absolutely no sense to me: not even the one statically typed to A
produces the A
-output any more!
Adding the dynamic
keyword to the definition or an override does seem to give me the desired behavior 'from that point in the class hierarchy downwards'...
Let's change our example to something a little less constructed, what actually made me post this question:
public class B: UIView {
}
extension B {
override public func layoutSubviews() { print("B") }
}
public class A: B {
}
extension A {
override public func layoutSubviews() { print("A") }
}
(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()
We now get A B A
. Here I cannot make UIView's layoutSubviews dynamic by any means.
Moving both overrides into their class declaration gets us A A A
again, only A's or only B's still gets us A B A
. dynamic
again solves my problems.
In theory I could add dynamic
to all override
s I ever do but I feel like I'm doing something else wrong here.
Is it really wrong to use extension
s for grouping code like I do?
Question&Answers:
os