I'm writing code in Swift, and using https://github.com/evermeer/EVReflection. However, Xcode is playing shenanigans with my class structure - in a few places, it claims I need to include a required initialized, declared in EVObject, but not in other places. Consider the following example:
class Root: EVObject {
}
class MidA: Root {
required init() {
}
init(blah: String) {
}
}
class LeafA: MidA {
required init() {
super.init()
}
} // Error: 'required' initializer 'init(coder:)' must be provided by subclass of 'EVObject'
class MidB: Root {
required init() {
}
}
class LeafB: MidB {
required init() {
super.init()
}
} // No error
EVObject contains the following method definition:
public convenience required init?(coder: NSCoder) {
self.init()
EVReflection.decodeObjectWithCoder(self, aDecoder: coder, conversionOptions: .DefaultNSCoding)
}
Describing the example in words, there's a root object, Root, which extends EVObject, and it forks into two subclasses, MidA and MidB, which each have a subclass of their own: LeafA and LeafB. LeafA and LeafB are identical aside from their name and superclass. MidA and MidB differ only in name and in that MidA has an additional initializer that takes a parameter.
What bearing could that possibly have on LeafA? Having an extra initializer with a parameter seems entirely unrelated to the particular initializer declared in EVObject (which is apparently required, but it's not usually enforced??). Why would adding an unrelated initialized in a branch class suddenly require me, in my leaf classes, to figure out what the heck is this required initializer I've never seen before?
It's indeed caused by the extra init
init(blah: String) {
}
That one could also be changed to:
convenience init(blah: String) {
self.init()
}
Then it won't complain about adding the required initializer.
With the convenience you specify that you will call a required initializer from there. Without that the compiler can't be sure that you do.
See also https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
Related
I got this code:
open class A: Decodable {
public init() {
}
}
open class B: A {
public override init() {
super.init()
}
open required init(from decoder: Decoder) throws {
fatalError("init(from:) has not been implemented")
}
}
And the almighty Xcode 9.4.1 tells me I need to change open to public before the requiered keyword. After I change open to public, the compiler tells me I need to change it to open. I can't get it to work while both classes are open, without my super class A explicitly implementing the required initializer as seen in class B. Why?
Decodable forces super open class to implement the initializer
If you don't inherit the required initializer the superclass has then you have to implement it yourself.
Required Initializers
Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer: <..>
You must also write the required modifier before every subclass implementation of a required initializer, to indicate that the initializer requirement applies to further subclasses in the chain.
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
How you can avoid implementing it yourself:
Initializer Inheritance
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Sources
Initialization #Required Initializers
Initialization #Automatic Initializer Inheritance
I have one base view controller and some child view controllers. I am having difficulty to pass the view model created in children view controller (CommentViewController) to it's parent view controller (FeedBaseViewController) to access.
class BaseViewController: UIViewController {
...
}
class FeedBaseViewController: BaseViewController {
var viewModel1 = FeedBaseViewModel<BaseObject>()
...
}
class CommentViewController: FeedBaseViewController {
var viewModel2: CommentViewModel<CommentObject>
init() {
viewModel2 = CommentViewModel()
/* This is where I have issue, and I tried a few approaches. And here are the 2 of them.*/
// Method 1:
// viewModel1 = viewModel2
// Error: Cannot assign value of type 'CommentViewModel<CommentObject>' to type 'FeedBaseViewModel<BaseObject>'
// Method 2:
// viewModel1 = viewModel2 as FeedBaseViewModel<BaseObject>
// Error: Cannot convert value of type 'CommentViewModel<CommentObject>' to type 'FeedBaseViewModel<BaseObject>' in coercion
...
}
...
}
My view models for the View Controller
class FeedBaseViewModel<Item: BaseObject> {
}
class CommentViewModel<T: CommentObject>: FeedBaseViewModel<CommentObject> {
}
Here are the object classes, the BaseObject class is the only Objective-C class here, and a CommentObject swift class which inherit from BaseObject.
#interface BaseObject : NSObject <NSCoding>
#end
class CommentObject: BaseObject {
}
I am not sure if it is possible to do, or it is only syntax issue.
Please advice, thank you!
Edited(2017-09-11):
I think I could provide more information of what I would like to achieve, and hope it helps.
FeedBaseViewModel stores and manipulate an array of items with type BaseObject.
FeedBaseViewController have UI handling actions such as "filtering", "sorting", and ask FeedBaseViewModel to do the job.
For the Comment part, I will have a CommentObject array with basic manipulation and some specific manipulation. This is why CommentViewModel comes in, CommentViewModel inherit FeedBaseViewModel so that basic manipulation can be handled by the parent, and it will do the specific manipulation itself.
Similar in the view controller part, CommentViewController will inherit FeedBaseViewController, so FeedBaseViewController can do the basic handling while CommentViewController itself can provide additional functions.
I do not think that is possible.
Your problem boils down to:
let m = FeedBaseViewModel<CommentObject>()
let k : FeedBaseViewModel<BaseObject> = m
That is not allowed. As to why: assume that FeedBaseViewModel contains a member of its generic type and offers a set function for that member. If you now would be allowed to write the second line, then call set using a BasObject instance that would violate the generic constraint of m because they are still the same object instance with different generic constraints, a BaseObject instance is not necessarily an instance of CommentObject.
A solution in your case should be to move the generics up a little bit more:
class FeedBaseViewController<T : BaseObject>: BaseViewController {
var viewModel1: FeedBaseViewModel<T>
required init?(coder aDecoder: NSCoder) {
viewModel1 = FeedBaseViewModel<T>()
super.init(coder: aDecoder)
}
}
class CommentViewController: FeedBaseViewController<CommentObject> {
var viewModel2: CommentViewModel<CommentObject>
required init?(coder aDecoder: NSCoder) {
viewModel2 = CommentViewModel<CommentObject>()
super.init(coder: aDecoder)
viewModel1 = viewModel2
// now this assignment is allowed because viewModel1 and viewModel2 have the super type FeedBaseViewModel<CommentObject>
}
}
Not sure if that solution works in your situation though.
If I declare
public class A: NSObject {
public class X { }
public init?(x: X? = nil) { }
}
all is fine. When using it like let a = A(), the initializer is called as expected.
Now, I'd like to have the nested class X private, and the parameterized init as well (has to be, of course). But a simple init?() should stay publicly available as it was before. So I write
public class B: NSObject {
private class X { }
private init?(x: X?) { }
public convenience override init?() { self.init(x: nil) }
}
But this gives an error with the init?() initializer: failable initializer 'init()' cannot override a non-failable initializer with the overridden initializer being the public init() in NSObject.
How comes I can effectively declare an initializer A.init?() without the conflict but not B.init?()?
Bonus question: Why am I not allowed to override a non-failable initializer with a failable one? The opposite is legal: I can override a failable initializer with a non-failable, which requires using a forced super.init()! and thus introduces the risk of a runtime error. To me, letting the subclass have the failable initializer feels more sensible since an extension of functionality introduces more chance of failure. But maybe I am missing something here – explanation greatly appreciated.
This is how I solved the problem for me:
I can declare
public convenience init?(_: Void) { self.init(x: nil) }
and use it like
let b = B(())
or even
let b = B()
— which is logical since its signature is (kind of) different, so no overriding here. Only using a Void parameter and omitting it in the call feels a bit strange… But the end justifies the means, I suppose. :-)
After a bit of fiddling I think I understand. Let's consider a protocol requiring this initializer and a class implementing it:
protocol I {
init()
}
class A : I {
init() {}
}
This gives the error: "Initializer requirement 'init()' can only be satisfied by a required initializer in non-final class 'A'". This makes sense, as you could always declare a subclass of A that doesn't inherit that initializer:
class B : A {
// init() is not inherited
init(n: Int) {}
}
So we need to make our initializer in A required:
class A : I {
required init() {}
}
Now if we look at the NSObject interface we can see that the initializer is not required:
public class NSObject : NSObjectProtocol {
[...]
public init()
[...]
}
We can confirm this by subclassing it, adding a different initializer and trying to use the normal one:
class MyObject : NSObject {
init(n: Int) {}
}
MyObject() // Error: Missing argument for parameter 'n:' in call
Now here comes the weird thing: We can extend NSObject to conform to the I protocol, even though it doesn't require this initializer:
extension NSObject : I {} // No error (!)
I honestly think this is either a bug or a requirement for ObjC interop to work (EDIT: It's a bug and already fixed in the latest version). This error shouldn't be possible:
extension I {
static func get() -> Self { return Self() }
}
MyObject.get()
// Runtime error: use of unimplemented initializer 'init()' for class '__lldb_expr_248.MyObject'
Now to answer your actual question:
In your second code sample, the compiler is right in that you cannot override a non-failable with a failable initializer.
In the first one, you aren't actually overriding the initializer (no override keyword either), but instead declaring a new one by which the other one can't be inherited.
Now that I wrote this much I'm not even sure what the first part of my answer has to do with your question, but it's nice to find a bug anyways.
I suggest you to do this instead:
public convenience override init() { self.init(x: nil)! }
Also have a look at the Initialization section of the Swift reference.
I am trying to understand the use of the required keyword in Swift classes.
class SomeClass
{
required init() {
// initializer implementation goes here
}
}
required doesn't force me to implement the method in my child-class. If I want to override the required designated initializer of my parent class I need to write required and not override. I know how it works but can not understand why I should do this.
What is the benefit of required?
As far as I can tell, languages like C# don't have something like this and work just fine with override.
It's actually just a way of satisfying the compiler to assure it that if this class were to have any subclasses, they would inherit or implement this same initializer. There is doubt on this point, because of the rule that if a subclass has a designated initializer of its own, no initializers from the superclass are inherited. Thus it is possible for a superclass to have an initializer and the subclass not to have it. required overcomes that possibility.
One situation where the compiler needs to be satisfied in this way involves protocols, and works like this:
protocol Flier {
init()
}
class Bird: Flier {
init() {} // compile error
}
The problem is that if Bird had a subclass, that subclass would have to implement or inherit init, and you have not guaranteed that. Marking Bird's init as required does guarantee it.
Alternatively, you could mark Bird as final, thus guaranteeing the converse, namely that it will never have a subclass.
Another situation is where you have a factory method that can make a class or its subclass by calling the same initializer:
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class NoisyDog: Dog {
}
func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
let d = whattype.init(name: "Fido") // compile error
return d
}
dogMakerAndNamer is calling the init(name:) initializer on Dog or a Dog subclass. But how can the compiler be sure that a subclass will have an init(name:) initializer? The required designation calms the compiler's fears.
According to the documentation:
Write the required modifier before the definition of a class initializer to
indicate that every subclass of the class must implement that initializer
So yes, required does force all child classes to implement this constructor. However, this is not needed
if you can satisfy the requirement with an inherited initializer.
So if you have created more complex classes that cannot be fully initialized with a parent constructor, you must implement the require constructor.
Example from documentation (with some added stuff):
class SomeClass {
required init() {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
let thisNeedsToBeInitialized: String
required init() {
// subclass implementation of the required initializer goes here
self.thisNeedsToBeInitialized = "default value"
}
}
I want to draw an attention on another solution provided by Required, apart from Matt has given above.
class superClass{
var name: String
required init(){
// initializer implementation goes here
self.name = "Untitled"
}
}
class subClass: superClass {
var neakName: String = "Subclass Untitled"
}
let instanceSubClass = subClass()
instanceSubClass.name //output: "Untitled"
instanceSubClass.neakName //output: "Subclass Untitled"
As you can check in above example, I've declared required init() on superClass, init() initializer of superClass has inherited by default on subClass, So you able to create an instance of subClass let instanceSubClass = subClass().
But, suppose you want to to add one designated initializer on subClass to assign run time value to stored property neakName. Of course you can add it, but that will result to no initializers from the superClass will be inherited to subClass, So if you will create an instance of subClass you will create through its own designated initializer as below.
class superClass{
var name: String
init(){
// initializer implementation goes here
self.name = "Untitled"
}
}
class subClass: superClass {
var neakName: String = "Subclass Untitled"
init(neakName: String) {
self.neakName = neakName
}
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name //output: "Untitled"
instanceSubClass.neakName //output: "Bobby"
Here above, you won't be able to create an instance of subClass by just subClass(), But if you want that every subclasses of superClass must have their own init() initializer to create direct instance by subClass(). Just place required keyword before init() on superClass, it will force you to add init() initializer on subClass too - as below.
class superClass{
var name: String
required init(){
// initializer implementation goes here
self.name = "Untitled"
}
}
class subClass: superClass {
var neakName: String = "Subclass Untitled"
init(neakName: String) {
self.neakName = neakName
}
} // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name //output: "Untitled"
instanceSubClass.neakName //output: "Bobby"
SO, use required keyword before initializer on superclass, when you want all subclasses must have been implemented required initializer of superclass.
If you are trying to add you own initialiser in the sub class, then you have to follow certain things those were declared in super class. So it make sure that you will not forget to implement that required method. If you forget compiler will give you error // fatal error, we've not included the required init()
. Another reason is it creates a set of conditions that ever sub class should follow it the sub class is defining its own initialiser.
I am writing method which takes a type which conforms to a protocol and instantiates an instance of this class. When I build it, the compiler crashes with a segfault. I appreciate that this points to a compiler bug 99% of the time, but I am interested to see if what I'm trying to do is logically correct or am I just throwing absolute nonsense at the compiler and I shouldn't be surprised to see it crash.
Here is my code
protocol CreatableClass {
init()
}
class ExampleClass : CreatableClass {
required init() {
}
}
class ClassCreator {
class func createClass(classType: CreatableClass.Type) -> CreatableClass {
return classType()
}
}
ClassCreator.createClass(ExampleClass.self)
I also tried to rule out passing a Type as a method parameter as being the root of the problem and the following code also crashes the compiler:
protocol CreatableClass {
init()
}
class ExampleClass : CreatableClass {
required init() {
}
}
let classType: CreatableClass.Type = CreatableClass.self
let instance = classType()
So - is this just a straightforward compiler bug and does what I am trying to do seem reasonable, or is there something in my implementation that is wrong?
Edit:
This can be achieved using generics as shown #Antonio below but unfortunately i believe that isn't useful for my application.
The actual non-dumbed down use-case for doing this is something like
protocol CreatableClass {}
protocol AnotherProtocol: class {}
class ClassCreator {
let dictionary: [String : CreatableClass]
func addHandlerForType(type: AnotherProtocol.Type, handler: CreatableClass.Type) {
let className: String = aMethodThatGetsClassNameAsAString(type)
dictionary[className] = handler()
}
required init() {}
}
I usually do that by defining a generic method. Try this:
class func createClass<T: CreatableClass>(classType: T.Type) -> CreatableClass {
return classType()
}
Update
A possible workaround is to pass a closure creating a class instance, rather than passing its type:
class ClassCreator {
class func createClass(instantiator: () -> CreatableClass) -> (CreatableClass, CreatableClass.Type) {
let instance = instantiator()
let classType = instance.dynamicType
return (instance, classType)
}
}
let ret = ClassCreator.createClass { ExampleClass() }
The advantage in this case is that you can store the closure in a dictionary for example, and create more instances on demand by just knowing the key (which is something in 1:1 relationship with the class name).
I used that method in a tiny dependency injection framework I developed months ago, which I realized it works only for #objc-compatible classes only though, making it not usable for my needs...