Closed Ti-m closed 4 years ago
See https://github.com/JetBrains/kotlin-native/blob/838cccf275b121878003d9c8c2f7600d78b10989/samples/uikit/src/iosMain/kotlin/ViewController.kt#L16 note @ExportObjCClass annotation.
If I convert the code to swift and run it directly in Xcode it works.
Actually this is not exactly true. Kotlin code
class TabBarBug : UITabBarController {
val innerNavDashboard = UINavigationController(rootViewController = UIViewController())
// ...
is not equivalent to Swift code
class TabBarBug : UITabBarController {
let innerNavDashboard = UINavigationController(rootViewController: UIViewController())
// ...
The difference is that innerNavDashboard
in Kotlin is initialized after super initializer, while innerNavDashboard
in Swift is initialized before super initializer.
And the problem here is that viewDidLoad
is called by super initializer, so in Kotlin innerNavDashboard
is not initialized by this moment.
The following Swift code is (roughly) equivalent to your Kotlin reproducer:
import UIKit
class TabBarBug : UITabBarController {
var innerNavDashboard: UINavigationController? = nil
init() {
super.init(nibName: nil, bundle: nil)
self.innerNavDashboard = UINavigationController(rootViewController: UIViewController())
}
// Required by compiler:
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func viewDidLoad() {
super.viewDidLoad()
let tabDashboard = UITabBarItem(title: "tabbaritem-dashboard", image: nil, selectedImage: nil)
self.innerNavDashboard!.tabBarItem = tabDashboard
self.viewControllers = [self.innerNavDashboard!]
}
}
It indeed fails.
See
note @ExportObjCClass annotation.
you mean this note right? https://github.com/JetBrains/kotlin-native/blob/54881b421cbdc784102d0023d93528f6523984b8/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCUtils.kt#L58
- Note: runtime lookup can be forced even when the class is referenced statically from
- Objective-C source code by adding
__attribute__((objc_runtime_visible))
to its@interface
.
I am not sure how to do this. Should this annotation appear in my Framework header if I add @ExportObjCClass to my class? Or do I have to add it by hand in my code?
————————————
The following Swift code is (roughly) equivalent to your Kotlin reproducer:
When I run your swift code it already crashes at:
self.innerNavDashboard!.tabBarItem = tabDashboard // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
The line with the crash in my first post is not even reached. So you mean my crash is caused by an uninitialized value? innerNavDashboard Is declared as an non-nullable reference. So this shouldn’t be possible?
From my first post:
this.innerNavDashboard.tabBarItem = tabDashboard //In case of null reference, it should crash already here? this.viewControllers = listOf(this.innerNavDashboard) //This line crashes with -[NSNull _setViewHostsLayoutEngine:]: unrecognized selector sent to instance 0x7fff805f0330
In my real code I first tried to pass the reference for the UINavigationController as a constructor argument like this:
class TabBarBugKotlin(private val navDashboard: UINavigationController) : UITabBarController(nibName = null, bundle = null) {
override fun viewDidLoad() {
super.viewDidLoad()
val tabDashboard = UITabBarItem(title = "tabbaritem-dashboard", image= null, selectedImage = null)
this.navDashboard.tabBarItem = tabDashboard
this.viewControllers = listOf(this.navDashboard) //crashes here
}
}
In this code the crash is happening too.
I am not sure how to do this. Should this annotation appear in my Framework header if I add @ExportObjCClass to my class? Or do I have to add it by hand in my code?
@ExportObjCClass
is likely not related to your issue and thus not actually required here.
The line with the crash in my first post is not even reached. So you mean my crash is caused by an uninitialized value?
Yes.
innerNavDashboard Is declared as an non-nullable reference. So this shouldn’t be possible?
In fact it is possible. Uninitialized values usually cause such problems.
In this code the crash is happening too.
Exactly, because val navDashboard: UINavigationController
is initialized after super initializer too.
For example, see the snippet in pure Kotlin showing this behaviour:
open class Base {
open fun foo() {}
init {
foo()
}
}
class Derived(val x: Any) : Base() {
override fun foo() {
println(x)
}
}
fun main() {
Derived(Any())
}
It prints null
on JVM too.
Ah you're right. The compiler even generates a warning in the base class. Of course I don't have the base class in this case. ;-) I replaced viewDidLoad with viewWillAppear and it works. Thank you very much.
Hey, when I run the following code I get a crash:
I think this is a bug because:
So the issue seems related to properties.