UPDATE: Question and title have been restated based on changes that were made in response to the initial question.
I've got 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)
}
}
I then implement a subclass of this base class with logic like the following
class MySubClass : MyBaseClass {
required init(_ record:MyRecordType) {
println("Entered MySubClass.init")
// Initialize subclass from record
super.init(record)
}
}
Next, I try to invoke the generic class method
class AnotherClass {
func successCallback(result:MySubclass) {
// Handle callback
}
MySubClass.retrieveInstance("objectName", callback:successCallback)
}
When creating an instance of the class - the line marked with a comment identifying the crash location - I am expecting the init method from MySubClass
to be invoked. (This odd notation is based on the bug workaround suggested in the replies)
Instead of calling the init
method for MySubClass
, this code crashes with a BAD_ACCESS. I have been unable to find any nil references or anything else that would explain this crash.
With a lot of help from the answer originally posted by @rintaro, I was able to solve this problem, although there is still an oddity that I will post under a separate question.
As it turns out, @rintaro was absolutely correct in the need to initialize the instance of the generic type using the following syntax:
let result = (T.self as T.Type)(myRecord)
This works as long as the base class MyBaseClass
declares this initializer with a required
tag:
public class MyBaseClass {
public required init(_ record:MyRecordType) {
...
}
}
and the subclass MySubClass
implements the matching initializer:
public class MySubClass : MyBaseClass {
public required init (_ record:MyRecordType) {
...
super.init(record)
}
}
Where things fail, however is when I actually have a 3-level class hierarchy and the initializer hierarchy throughs an override
into the mix. To envision this, consider a set of class that represents 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 in the implementation of the RootNode
class's retrieveAll
method. In order for it to work as described by @rintaro, I need the init
in RootNode
to be marked with the required
keyword. But because it also overrides the initializer from Node
, it also needs to have the override
keyword. So I try to use both keywords in the declaration:
override required public init(_ record:MyRecordType) {
...
}
The Swift compiler accepts this, but when I use it to initialize an instance from within the retrieveAll
method, it crashes with a BAD_ACCESS.
I was able to work around this problem by changing the method signature of the NodeClass
just slightly so that its RootNode
subclass doesn't need to override:
public class Node {
public init(record:MyRecordType) {
...
}
}
public class RootNode {
public required init(_ record:MyRecordType) {
...
super.init(record:record)
}
}
By doing this workaround, my subclasses of RootNode
can properly implement the required initializer, and the retrieveAll
method in RootNode
can properly instantiate instances of those subclasses.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With