Prior to WWDC 2014 I was learning Objective-C. I had created an "Elevator Simulator" app that exists simply for the purpose of my learning. When you click one of the seven buttons, it closes the elevator doors (animation), moves one level at a time towards the selected level (as indicated by a text label and button colouring) and then re-opens the doors.
Following WWDC and the announcement of Swift, I'm trying to re-create this app in Swift. So far I'm finding Swift much nicer to work with, but I've hit one major roadblock concerning unowned references. The app was launching fine until I implemented the feature for the text label and button colouring to set based on properties of the Elevator model on app launch. After implementing these features, the app always crashes on launch.
Eventually I narrowed down the issue to being caused on any call from the view controller to the Elevator model. After commenting out the call to my view controller method for updating the interface, the app launches fine again, and I can confirm that the issue is with calling the Elevator model simply by trying to call in the initialiser. The following code snippets reproduce the issue:
App Delegate:
var window: UIWindow?
let mainElevator = Elevator()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
mainElevator.view = ViewController(elevator: mainElevator)
window!.rootViewController = mainElevator.view
window!.makeKeyAndVisible()
return true
}
Elevator (model):
class Elevator: NSObject {
var view: ViewController?
var currentLevel = 1
var newLevel: Int?
}
ViewController:
class ViewController: UIViewController {
unowned let elevator: Elevator
init(elevator: Elevator) {
self.elevator = elevator
super.init(nibName: nil, bundle: nil)
//self.elevator.currentLevel = 3
}
}
Note: I've emitted the other methods in these classes as they don't seem to have an impact on the issue. I have confirmed this by creating a similar scenario using a Person and CreditCard example (used as an example by Apple), which simply consist of the parts I've pasted above.
With that last line of ViewController commented out, the app launches fine (showing the view based on loadView() and viewDidLoad()), but if I uncomment it, the app crashes. If I change the reference type to weak and optional, it also works fine, but it shouldn't be weak, optional, or a variable. The Elevator will always exist (but not necessarily the view, due to multi-tasking), and it should be constant. This is exactly the kind of situation that Apple described in the Person/CreditCard scenario.
Also, if I remove self. from that last tine (therefore using the local version of elevator), it works fine. So the model isn't being deallocated, it's just the unowned reference that's not working.
Edit: Crash Report
libswift_stdlib_core.dylib`_swift_abortRetainUnowned:
0x1001c3980: pushq %rbp
0x1001c3981: movq %rsp, %rbp
0x1001c3984: leaq 0x176a7(%rip), %rax ; "attempted to retain deallocated object"
0x1001c398b: movq %rax, 0x792ce(%rip) ; gCRAnnotations + 8
0x1001c3992: int3
0x1001c3993: nopw %cs:(%rax,%rax) Thread 1: EXC_BREAKPOINT (code=EXT_1386_BPT, subcode=0x0)