Safety of unowned in swift - swift

I was reading about unowned from the apple docs.
Like weak references, an unowned reference does not keep a strong hold
on the instance it refers to. Unlike a weak reference, however, an
unowned reference is assumed to always have a value. Because of this,
an unowned reference is always defined as a nonoptional type.
So it seems unowned are similar to weak but they are non optionals.
I was wondering what will happen if when the referenced unowned is deallocated. Why is there no checks like optionals.
I mean I can do something like this,
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var card: CreditCard? = nil
do {
var john: Customer
john = Customer(name: "John Appleseed")
john.card = CreditCard(number: 1234_5678_9012_3456, customer: john)
card = john.card
}
print("Card belongs to \(card?.customer.name)")
In the last line assuming an unowned to always have a value try to print the name of the card holder, and I got a "Execution was interrupted, reason: EXE_BREAKPOINT..."
I suppose there should not be a problem like this or there should be some kind of safety check at line card = john.card

The checked version of unowned is already there - it is called weak. That is the main difference. unowned is unsafe, but it can make your code clearer is some cases.

unowned references are useful for breaking reference cycles as an alternative to weak when (as the Apple Docs you referenced says) "you know that the reference will never be nil once it has been set during initialisation."
However you have fabricated an example of setting an unowned reference to an object which then goes out of scope making the unowned reference nil.

Related

Swift Reference Cycle When Capturing A Computed String Property Declared From Inside An Instance Method

I'm implementing an in-game store using Swift and SpriteKit. There is a class called Store which has a method setupItems() inside of which we declare and instantiate instances of a class StoreItem and also add each store item instance as a child of Store. Each StoreItem has an optional closure property called updateInventory which is set inside of setupItems() as well. This is an optional closure because some items don't have a limited inventory.
Store also has an unowned instance property storeDelegate which is responsible for determining how funds are deducted and how storeItems are applied once purchased. storeDelegate is unowned as it has an equal or greater lifetime than Store.
Now, here is where things get interesting - the closure updateInventory references a computed string variable called itemText which makes a calculation based on a property of storeDelegate. If itemText is declared and instantiated as a variable inside of setupItems() we have a reference cycle and store is not deallocated. If instead, itemText is declared and instantiated as an instance property of Store (right under the property unowned storeDelegate that it references) then there is no reference cycle and everything deallocates when it should.
This seems to imply that referencing storeDelegate from a computed variable inside an instance method of a class doesn't respect the unowned qualifier. Code examples follow:
Current Scenario
protocol StoreDelegate: AnyObject {
func subtractFunds(byValue value: Int)
func addInventoryItem(item: InventoryItem) throws
var player: Player! { get }
}
class Store: SKSpriteNode, StoreItemDelegate {
unowned var storeDelegate: StoreDelegate
init(storeDelegate: StoreDelegate) {
self.storeDelegate = storeDelegate
setupItems()
...
}
func setupItems() {
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem = StoreItem(name: "coolItem")
storeItem.updateInventory = {
[unowned storeItem, unowned self] in
// some logic to check if the update is valid
guard self.storeDelegate.player.canUpdate() else {
return
}
storeItem.label.text = itemText
}
}
The above leads to a reference cycle, interestingly if we move
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
outside of updateItems and make it an instance variable of Store right below unowned var storeDelegate: StoreDelegate, then there is no reference cycle.
I have no idea why this would be the case and don't see mention of it in the docs. Any suggestions would be appreciated and let me know if you'd like any additional details.
storeItem.updateInventory now keeps strong reference to itemText.
I think the issue is that itemText holds a strong reference to self implicitly in order to access storeDelegate, instead of keeping a reference to storeDelegate. Anothe option is that even though self is keeping the delegate as unowned, once you pass ot to itemText for keeping, it is managed (ie, strong reference again).
Either way, you can guarantee not keeping strong reference changing itemText to a function and pass the delegate directly:
func setupItems() {
func itemText(with delegate: StoreDelegate) -> String
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem...
storeItem.updateInventory = {
// ...
storeItem.label.text = itemText(with self.storeDelegate)
}
}

Does ARC deallocate a class if deinit has been declared or does it maintain the reference?

I'm trying to wrap my head around using ARC and the way it handles deallocation of memory. If I have some class:
class Person {
var name: String = ""
var age: Int = 0
init(name: String, age: Int) {
self.name = name
self.age = age
}
deinit {
print("Person \(name) has been deallocated")
}
}
class MyViewController: UIViewController {
var person = Person(name: "Steve", age: 85)
}
Do I need to worry about freeing up the space for the person or does ARC handle it? Do I need to declare a deinit in MyViewController?
ARC handles deinitialization and deallocation for you once the referencing object is itself deallocated. Quoting from https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html:
…ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.
…whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong” reference because it keeps a firm hold on that instance, and doesn’t allow it to be deallocated for as long as that strong reference remains.
Your code demonstrates this as well: if I copy your code into a playground and add MyViewController() to create an instance of the class, I see Person Steve has been deallocated in the console output before the program exits.

Why can I access an object after its deinit method has been called?

I copied most of this code from the Unowned References section in the Swift Guide and ran it in a playground...
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being “deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
var card = john!.card!
john = nil
card.customer.name
john set to nil results in...
"John Appleseed is being “deinitialized\n"
But then getting the name property gives...
"John Appleseed"
So, the customer instance is still accessible despite having being deinitialized!
Shouldn't this result in a nil reference exception? It says in the guide...
Note also that Swift guarantees your app will crash if you try to
access an unowned reference after the instance it references is
deallocated. You will never encounter unexpected behavior in this
situation. Your app will always crash reliably, although you should,
of course, prevent it from doing so.
So, I presume it must not have been deallocated. And yet, the guide also says,
A deinitializer is called immediately before a class instance is
deallocated.
The code works as expected in a regular project, as opposed to a playground.

What's different between ? and ! in weak, strong reference in Swift

I'm beginner in Swift. I have some questions need to resolve but I can't do it by myself.
Here is some problem for me:
class Author {
weak var book: Book?
deinit {
print("Dealloc Author")
}
}
class Book {
var author: Author?
deinit {
print("Dealloc Book")
}
}
var authorObj:Author? = Author()
authorObj!.book = Book()
authorObj!.book!.author = authorObj
This compiles fine:
class Author {
weak var book: Book?
deinit {
print("Dealloc Author")
}
}
class Book {
var author: Author?
deinit {
print("Dealloc Book")
}
}
var authorObj:Author? = Author()
authorObj!.book = Book()
authorObj!.book?.author = authorObj
authorObj = nil
So can you guys explain for me, what's different between ? and ! in authorObj!.book?.author = authorObj and
authorObj!.book!.author = authorObj?
I have two more questions:
authorObj is a strong reference same as authorObj.book.author, it's strong reference too? Because it dont have weak or unowned before var.
Only authorObj.book is weak reference. But when I assign authorObj to nil, all are deinited. Why? I assign only authorObj to nil but Author() instance still have 1 strong reference authorObj.book.author
So can you guys explain for me, what's different between ? and ! in
authorObj!.book?.author = authorObj and authorObj!.book!.author =
authorObj?
When you use ? to unwrap an optional it is referred to as optional chaining. If the optional is nil, the result of the entire chain will be nil. The advantage of using ? is that your app won't crash if the value being unwrapped is nil.
So:
authorObj!.book?.author = authorObj
will crash if authorObj is nil (because of the forced unwrap !).
and:
authorObj!.book!.author = authorObj
will crash if either authorObj or book is nil.
The safe way to write this would be:
authorObj?.book?.author = authorObj
If authorObj or book is nil, this will do nothing and it won't crash.
authorObj is a strong reference same as authorObj.book.author, it's
strong reference too? Because it dont have weak or unowned before var.
It only makes sense to talk about a single variable when talking about weak vs. strong. It doesn't make sense to ask if authorObj.book is weak; you can say that Author holds a weak reference to book.
Only authorObj.book is weak reference. But when I assign authorObj to
nil, all are deinited. Why? I assign only authorObj to nil but
Author() instance still have 1 strong reference authorObj.book.author
When you assign nil to authorObj, that was the last strong reference to authorObj, so Automatic Reference Counting (ARC) decrements the reference counter and then frees all of the references inside of authorObj. If those are strong references, it decrements the reference count and if that was the last reference to that object, the object is freed as well. If any other object is holding a weak reference to any object that is freed, then ARC will set that value to nil in all of the weak pointers.
To test this in a playground, put your commands inside a function called test and add print statements so that you can see when things happen.
class Author {
weak var book: Book?
deinit {
print("Dealloc Author")
}
}
class Book {
var author: Author?
deinit {
print("Dealloc Book")
}
}
func test() {
print("one")
var authorObj: Author? = Author()
print("two")
authorObj!.book = Book()
print("three")
authorObj!.book?.author = authorObj
print("four")
}
test()
Output:
one
two
Dealloc Book
three
four
Dealloc Author
The thing to note is that the Book is deallocated before step three. Why? Because there are no strong pointers to it. You allocated it and then assigned the only reference to it to a weak pointer inside of Author, so ARC immediately freed it.
That explains why authorObj!.book!.author = authorObj crashes, because authorObj!.book is nil since the Book which was just assigned to it has been freed.
Now, try assigning Book() to a local variable book:
func test() {
print("one")
var authorObj: Author? = Author()
print("two")
let book = Book()
authorObj!.book = book
print("three")
authorObj!.book?.author = authorObj
print("four")
authorObj = nil
print("five")
}
test()
This time, the output is quite different:
one
two
three
four
five
Dealloc Book
Dealloc Author
Now, the local variable book holds a strong reference to the Book that was allocated, so it doesn't get immediately freed.
Note, even though we assigned nil to authorObj in step four, it wasn't deallocated until after book was deallocated after step five.
The local variable book holds a strong reference to Book(), and Book holds a strong reference to Author, so when we assign nil to authorObj in step four, the authorObj can't be freed because book still holds a strong reference to it. When test ends, the local variable book is freed, so the strong reference to authorObj is freed, and finally authorObj can be deallocated since the last strong reference to it is gone.

Weak references in Swift playground don't work as expected

I have been following the weak referencing example from the Intermediate Swift WWDC session in a Playground. I modified the code slightly as follows:
class Apartment {
let address: Int
init(address: Int) {
self.address = address
}
weak var tenant: Person?
}
class Person {
let name: String
init(name: String){
self.name = name
}
weak var home: Apartment?
func moveIn(apt: Apartment) {
self.home = apt
apt.tenant = self
}
}
var renters = ["John Appleseed": Person(name: "John Appleseed")]
var apts = [16: Apartment(address: 16)]
renters["John Appleseed"]!.moveIn(apts[16]!)
renters["John Appleseed"] = nil // memory should be released here
// then apts[16].tenant should be nil
if let tenantName = apts[16]!.tenant?.name {
// this should only execute if the Person object is still in memory
println("\(tenantName) lives at apartment number \(apts[16]!.address)")
} else {
// and this line should execute if the memory is released as we expect
println("Nobody lives at apartment number \(apts[16]!.address)")
}
// Console output in Playground: John Appleseed lives at apartment number 16
// Console output in standalone app: Nobody lives at apartment number 16
From my understanding of weak referencing, the memory allocated for the instance of Person should be released when it is removed from the renters dictionary because the only other reference to it is weak. However, the output of the programme is different if it is run as a standalone command line application vs. in a Playground (see comments).
I believe the top-level function (REPL/playground) is keeping a strong reference to facilitate interactive behavior, and cleaning up when the frame returns. This behavior eliminates memory leaks in the interactive environment.
I copied Viktor's simple example and used the xcrun swift REPL.
In REPL mode, I wrapped the logic in a function and it works as expected. If/when you care when the memory is cleaned up, I would suggest wrapping your logic in a function.
// declaration of the types
class Person {
let name: String
weak var home: Apartment?
init(pName: String){
name = pName
}
}
class Apartment {
let postalCode: Int
init(pPostalCode: Int) {
postalCode = pPostalCode
}
}
func testArc() {
// create Person object
var personJulius: Person = Person(pName: "Julius")
// create Apartment object
var apartmentBerlin: Apartment? = Apartment(pPostalCode: 10777)
// connect Apartment object and Person object
personJulius.home = apartmentBerlin
// Set only strong reference of Apartment object to nil
apartmentBerlin = nil
// Person object should now have nil as home
if personJulius.home != nil {
println("Julius does live in a destroyed apartment")
} else {
println("everything as it should")
}
}
//outputs "everything as it should"
testArc()
I guess that the Playground itself keeps a strong reference to the object, so the code behaves differently? If that's the case, this could cause some unexpected problems!
I tried an a bit less complex setup of the code.
But did have the same problem in the Playground file, but not in a real command line project.
In a command line project, the output was everything as it should, and in the playground it was Julius does live in a destroyed apartment.
import Cocoa
// declaration of the types
class Person {
let name: String
weak var home: Apartment?
init(pName: String){
name = pName
}
}
class Apartment {
let postalCode: Int
init(pPostalCode: Int) {
postalCode = pPostalCode
}
}
// create Person object
var personJulius: Person = Person(pName: "Julius")
// create Apartment object
var apartmentBerlin: Apartment? = Apartment(pPostalCode: 10777)
// connect Apartment object and Person object
personJulius.home = apartmentBerlin
// Set only strong reference of Apartment object to nil
apartmentBerlin = nil
// Person object should now have nil as home
if personJulius.home != nil {
println("Julius does live in a destroyed apartment")
} else {
println("everything as it should")
}
It is not only weak reference. In playground, the deinit does not work. Since set a variable to nil can not let deinit run, it is not the time weak reference should work.
class MyClass {
init() {
println("ready")
}
deinit {
println("OK")
}
}
var aClass: MyClass?
aClass
aClass = MyClass()
aClass = nil
Update 3: As of 11.3.1: Playground seems to be deiniting objects as expected, as far as I can tell. My original and out of date answer follows: In Xcode 10.1 Playgrounds, I can confirm that deinits are still behaving strangely and I can't use Playgrounds to test whether things are being properly deallocated. Update 1: From another similar thread, I learned that Xcode>New>Project>macOS>Command Line Tool, is a relatively lightweight way to create a generic testing environment that works fine for testing deallocation.
class Person {
let name: String
init(named name: String) { self.name = name }
var house: House?
deinit { print("\(name) is being deinitialized") }
}
class House {
let address: String
init(address: String) { self.address = address }
weak var tenant: Person?
deinit { print("House \(address) is being deinitialized") }
}
func move(_ person: Person, into house: House){
house.tenant = person
person.house = house
}
When Person and House are unconnected, deinits work properly.
However, if I move Buffy into the house, and delete Buffy, because tenant is weak, the Buffy object should be deinited and tenant set to nil. As you can see neither happens.
Even after I delete house (line 38), neither are deinited.
Weak references are behaving like strong references in the Playground.
Wrapping the Run code in a function does not change anything in this example. Update 2: In Xcode 11, As wbennet suggests above, if you wrap your run code in a func and call it, deallocations work for weak references as defined, in Playground.