Swift Weak Reference Much Slower than Strong Reference
As I was writing up/investigating this issue, I eventually found a solution. To have a simple back pointer without the overhead checks of weak
or unowned
you can declare body as:
unowned(unsafe) var body: Body!
According to the Swift documentation:
Swift also provides unsafe unowned references for cases where you need
to disable runtime safety checks—for example, for performance reasons.
As with all unsafe operations, you take on the responsibility for
checking that code for safety.You indicate an unsafe unowned reference by writing unowned(unsafe).
If you try to access an unsafe unowned reference after the instance
that it refers to is deallocated, your program will try to access the
memory location where the instance used to be, which is an unsafe
operation
So it is clear these runtime checks can produce serious overhead in performance-critical code.
Update:
As of Swift 5.2 (Xcode 11.4), I have noticed that unowned(unsafe)
has much more overhead. I now simply use strong references and break retain cycles manually, or try to avoid them entirely in performance-critical code.
Note: This is still true as of Xcode 12/Swift 5.3
Swift: keeping a strong reference to a weak variable
While I wholeheartedly agree with Rob Napier’s analysis, for the sake of completeness, I should note that you can also make the lifetime of the object explicit:
let delegate = InterimDelegate()
withExtendedLifetime(delegate) {
let document = MyDocument()
document.delegate = delegate
document.save()
}
What's different between ? and ! in weak, strong reference in Swift
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 vs unowned in Swift. What are the internal differences?
My question is, what is the point in having two such similar concepts? What are the internal differences that necessitate having two keywords for what seem essentially 99% the same thing?
They are not at all similar. They are as different as they can be.
weak
is a highly complex concept, introduced when ARC was introduced. It performs the near-miraculous task of allowing you to prevent a retain a cycle (by avoiding a strong reference) without risking a crash from a dangling pointer when the referenced object goes out of existence — something that used to happen all the time before ARC was introduced.unowned
, on the other hand, is non-ARC weak (to be specific, it is the same as non-ARCassign
). It is what we used to have to risk, it is what caused so many crashes, before ARC was introduced. It is highly dangerous, because you can get a dangling pointer and a crash if the referenced object goes out of existence.
The reason for the difference is that weak
, in order to perform its miracle, involves a lot of extra overhead for the runtime, inserted behind the scenes by the compiler. weak
references are memory-managed for you. In particular, the runtime must maintain a scratchpad of all references marked in this way, keeping track of them so that if an object weakly referenced goes out of existence, the runtime can locate that reference and replace it by nil
to prevent a dangling pointer.
In Swift, as a consequence, a weak
reference is always to an Optional (exactly so that it can be replaced by nil
). This is an additional source of overhead, because working with an Optional entails extra work, as it must always be unwrapped in order to get anything done with it.
For this reason, unowned
is always to be preferred wherever it is applicable. But never use it unless it is absolutely safe to do so! With unowned
, you are throwing away automatic memory management and safety. You are deliberately reverting to the bad old days before ARC.
In my usage, the common case arises in situations where a closure needs a capture list involving self
in order to avoid a retain cycle. In such a situation, it is almost always possible to say [unowned self]
in the capture list. When we do:
It is more convenient for the programmer because there is nothing to unwrap.
[weak self]
would be an Optional in need of unwrapping in order to use it.It is more efficient, partly for the same reason (unwrapping always adds an extra level of indirection) and partly because it is one fewer weak reference for the runtime's scratchpad list to keep track of.
Why weakifying a strong reference by using a local variable doesn't work?
When assigning (a = b
), you can't control what kind of reference a
is. You can only control what object a
refer to.
Here:
d[u.uuid] = u
The code is not saying:
The value associated with the key
u.uuid
ind
is set to the (unowned) referenceu
.
It's saying:
The value associated with the key
u.uuid
ind
is a reference (not saying what kind) that refers to whatu
is referring to.
The fact that u
is unowned is rather irrelevant. Dictionaries will always store strong references to objects. That's just how they are designed.
In the case of the Weak<T>
wrapper, the dictionary will still store a strong reference to Weak<T>
, but Weak<T>
will store a weak reference to the wrapped T
object. That's how it achieves not having a strong reference to the T
object.
In Swift, unowned vs. weak reference
Strong & Weak references
A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance.
So when A has a weak reference to B, then A is NOT the owner.
Example (where A is Bone
and B is Dog
)
class Dog {
var bone: Bone?
}
class Bone {
weak var belongsTo: Dog?
}
Strong Reference
Here a Dog
can have a Bone
. In that case it's the owner of that Bone. So the bone
property is a strong reference.
Weak reference
The Bone
can belong to a Dog. But we need to declare the belongsTo
property as weak, otherwise we have a strong retain cycle (which means ARC is not going to release these object once we are done with them).
Important: In this scenario a
Dog
can exists without aBone
. And aBone
can exists without aDog
.
Unowned reference
Let's look at another example
class Person {
var creditCard: CreditCard?
}
class CreditCard {
unowned var owner: Person
init(owner: Person) {
self.owner = owner
}
}
Again, Person can own a CreditCard, so it has a the owner property which is a strong reference to CreditCard
.
However a
CreditCard
CANNOT exist without a person. Right?
So insideCreditCard
we want a property which will always be populated but we also want it to be weak.
Something like this
weak var owner: Person
error: 'weak' variable should have optional type 'Person?'
However a weak property must be declared as Optional
so we use the unowned
which means:
I want a weak reference and it will always be populated.
Understanding weak and unowned reference in Swift under the hood
Okay, i found out.
- Unowned link points to the object as well as strong.
- Unowned link faster than weak.
- Side table stores strong RC, weak RC, unowned RC, link to the object and some flags
Weak or Unowned or None
This does not create a strong reference cycle, because you used weak self
.
ViewController
holds a strong reference to ViewModel
. ViewModel
holds a strong reference to a closure. The closure holds a weak reference to the ViewController
:
VC ---strong---> ViewModel
^ |
| strong
| v
--------weak-----closure
As long as ViewController
is deallocated (this happens when you dismiss it for example), ViewModel
will be as well.
Swift: Can weak reference be deallocated during optional binding?
This entire structure, which dates back way into the Objective-C days, is traditionally called the "weak–strong dance". Let me explain the purpose of the dance.
Our overall purpose is: avoid the danger of a retain cycle and memory leak. That is why we say weak somevariable
.
Having done that, however, we do indeed introduce the danger that the object pointed to by somevariable
might be deallocated. However, we deal coherently with that danger, by saying if let
. Here's how:
The object might have been deallocated by the time we enter the first curly braces in your code. But that's not a problem. The
if let
means that if the object has been deallocated, then in that case we getnil
and we do nothing (we never enter the second curly braces).If the object has not been deallocated by the first curly braces, then the
if let
succeeds, and then, as Cristik says, theif let
creates a strong reference and now we enter the second curly braces with a guarantee that the object will persist for their entirety.
Thus, we get coherent and consistent behavior.
SomeLib.someAsyncFunction { // someVariable might be deallocated...
[weak someVariable] in // ...and that is the point of `weak`, to allow that
if let someVariableU = someVariable { // find out whether someVariable was deallocated
// if we get here, someVariable was _not_ deallocated...
// and someVariableU is a strong reference and persists thru this block
}
}
Related Topics
Firebase Says That My Rules Are Insecure, Why
Using Guard with a Non-Optional Value Assignment
Rxswift/Rxcocoa: Prevent Uitextfield from Having More Than ... Characters
How to Change the Uinavigationbar Title's Position
What Does <> (Angle Brackets) Do on Class Names in Swift
How to Set Countdowntimer Mode in Datepicker on Swiftui
Realm Mobile Platform, How to Connect While Offline
Calling a Global Function Which Has the Same Name as a Member Function
Swift Extension for Selected Class Instance
Using Swift Library in Xamarin
Swift: Popover Dismiss Callback
What Is the "@Exported" Attribute in Swift
Input Type=File Not Working in Webview of Os X Application
Lazy Readonly Property in Swift
Enum with Identical Cases Names with Associated Values of Different Types
How to View Value of Swift "Let" Constant in Xcode 6 Debugger