I have subclasses of UINavigationController
and UITableViewController
.
To initialize subclasses I decided to use some convenience init
methods that call some designated initializer of the superclass. Also, each subclass has some let
constant:
let someValue: SomeClass = SomeClass()
Each class is successfully initialized by calling its newly created convenience init
method.
The problem is that the let
constant is initialized TWICE in UINavigationController
subclass.
import UIKit
import PlaygroundSupport
final class Navigation: UINavigationController {
convenience init(anyObject: Any) {
self.init(rootViewController: UIViewController())
}
let service = Constant("Constant Initialization -> Navigation")
}
final class Table: UITableViewController {
convenience init(anyObject: Any) {
self.init(style: .plain)
}
let service = Constant("Constant Initialization -> Table")
}
class Constant: NSObject {
init(_ string: String) {
super.init()
debugPrint(string)
}
}
Navigation(anyObject: NSNull())
Table(anyObject: NSNull())
Are we allowed to use convenience init
like above? Why?
Why is the convenience init
behavior is different in these two cases?
Checked with: Version 8.2 beta (8C30a), Version 8.2 (8C38), Version 8.2.1 (8C1002)
P.S. Playground log of the code above:
"Constant Initialization -> Navigation"
"Constant Initialization -> Navigation"
"Constant Initialization -> Table"
So this seems to be weirdly "expected behavior" with Swift. The reason it's happening is due to the constant initialized property service
. Namely, this post summarizes the issue your seeing pretty well: blog post
Essentially, the Obj-C underlying super classes are leaking memory and your service
property is initialized twice because of this pattern of Obj-C initialization:
- (id)init {
self = [super init];
if (self) {
self = [[self.class alloc] initWithNibName:nil bundle:nil];
}
return self;
}
A simple solution to avoid this is the following pattern:
import UIKit
final class NavigationController: UINavigationController {
var service: ObjectTest?
convenience init() {
self.init(rootViewController: UIViewController())
self.service = ObjectTest("init nav")
}
}
class ObjectTest: NSObject{
init(_ string: String) {
super.init()
print(string)
}
}
All that's changing from your implementation is initializing the service
only after your class itself is initialized.
Another solution, though, is to use the designated initializer for the superclass your initializing. Meaning that you don't use a convenience initializer which would employ the above Obj-C initialization pattern.
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