Failed to call initializer for subclass of generic type

UPDATE: The question and title were recounted based on the changes made in response to the original question.


I have a base class that implements a generic class method for creating new instances. The simplified logic of this class method is as follows

class MyBaseClass { required init(_ record:MyRecordType) { println("Entered MyBaseClass.init") // Initialize base class from record } class func retrieveInstance<T:MyBaseClass>(name:String, callback:(T) -> ()) { let myRecord:MyRecordType = ... // Perform fetch to get a record for name let result = (T.self as T.Type)(myRecord) // Code currently crashes with BAD_ACCESS at this line callback(result) } } 

Then I implement a subclass of this base class with logic as follows

 class MySubClass : MyBaseClass { required init(_ record:MyRecordType) { println("Entered MySubClass.init") // Initialize subclass from record super.init(record) } } 

Then I try to call a generic class method

 class AnotherClass { func successCallback(result:MySubclass) { // Handle callback } MySubClass.retrieveInstance("objectName", callback:successCallback) } 

When creating an instance of the class — a line marked with a comment identifying the location of the failure — I expect the init method from MySubClass be called. (This odd notation is based on the error workaround suggested in the answers)

Instead of calling the init method for MySubClass , this code fails BAD_ACCESS. I could not find pitiful links or anything else that could explain this collapse.

+5
source share
1 answer

With a lot of help from the answer originally posted by @rintaro, I was able to solve this problem, although there is still a weirdness that I will post on a separate issue.

As it turns out, @rintaro was absolutely right in having to initialize an instance of a type type using the following syntax:

 let result = (T.self as T.Type)(myRecord) 

This works while the base class MyBaseClass declares this initializer with the required tag:

 public class MyBaseClass { public required init(_ record:MyRecordType) { ... } } 

and the subclass MySubClass implements the corresponding initializer:

 public class MySubClass : MyBaseClass { public required init (_ record:MyRecordType) { ... super.init(record) } } 

If something fails, however, when I really have a class hierarchy of 3 levels, and the initializer hierarchy through override to the mix. To represent this, consider a set of classes representing nodes in a tree structure:

 public class Node { public init(_ record:MyRecordType) { ... } } public class RootNode : Node { override public init(_ record:MyRecordType) { ... super.init(record) } public class func <T:RootNode>retrieveAll(success:(T) -> ()) { // Retrieve all instances of root node subclass T, and invoke success callback with new T instance } } public class ChildNode : Node { public init(_ record:MyRecordType, parentNode:Node) { ... super.init(record) } public class func <T:ChildNode>retrieveChildren(parent:Node, success:(T) -> ()) { // Retrieve all child T instances of parent node, and invoke success callback with new T instance { } 

The problem occurs when implementing the RootNode class retrieveAll method. In order for it to work as described by @rintaro, I need init in the RootNode so that it is marked with the required keyword. But since it also overrides the initializer from Node , it must also have the override keyword. Therefore, I am trying to use both words in a declaration:

 override required public init(_ record:MyRecordType) { ... } 

The Swift compiler accepts this, but when I use it to initialize an instance from the retrieveAll method, it fails with BAD_ACCESS.

I managed to get around this problem by slightly changing the signature of the NodeClass method NodeClass that its subclass RootNode does not need to be redefined:

 public class Node { public init(record:MyRecordType) { ... } } public class RootNode { public required init(_ record:MyRecordType) { ... super.init(record:record) } } 

In this case, my RootNode subclasses can correctly implement the required initializer, and the retrieveAll method in RootNode can correctly instantiate these subclasses.

+5
source

Source: https://habr.com/ru/post/1207629/


All Articles