Swift semantics calling a method on a (nullable) weak reference using the ?. operator - swift

Back in objective-C with ARC, this wasn't safe to do:
MyClass* someObject = ...
__weak MyClass* weakSomeObject = someObject;
doSomething(^{
[weakSomeObject someMethod];
});
Why? because simply calling a method doesn't cause ARC to retain the object, and thus the someObject instance might be released and dealloced in the middle of the execution of someMethod
Bringing this forward into swift, it translates as follows:
let someObject: MyClass = ...
doSomething { [weak someObject]
someObject?.someMethod()
}
My question is, what are the semantics of the ?. operator in swift regarding ARC, and is it safe to use with weak reference method calls?
I can imagine the swift compiler translating the above code into something like this:
let someObject: MyClass = ...
doSomething { [weak someObject]
if let tmp = someObject {
tmp.someMethod()
}
}
If it did that, then it would indeed be safe, as tmp is a strong reference and thus would cause ARC to retain the object across the duration of the call to someMethod
However, I could also imagine it translating into something without an ARC retain for performance reasons or whatever.
Does anyone know what it actually does, and is there a specification or document that makes this explicit?

Bullet points to get first:
closures capture context.
if let, guard etc only checks if optional contains actual value but doesn't capture it's context.
Now about ?. It's optional chaining, if object on which method is called is nil, it won't call method as well as fail full chain(if you follow up return value from method with another method call or else).
About weak and unowned references to used objects inside closure:
weak doesn't create strong reference to object, so it makes it optional because object may not be there when it gets called
unowned as well as weak doesn't create strong reference, however object won't be optional, so you must guarantee that closure won't outlive object it uses, because you will get crash referencing object that isn't there.
Hope it helps.

Short answer is yes, it is safe. Longer explanation and link follow:
Methods defined for a type in Swift are curried functions like this:
class MyClass {
func doSomething() {
// I can access self in here
}
}
print(type(of: MyClass.doSomething)) //(MyClass) -> () -> ()
In other words, the function defined on the type is curried such that it must first be passed a reference to what will be self, which is strongly captured in the returned partially applied function (()->())
When you call the method on an instance of MyClass (not the type itself), it automatically does the initial partial application of passing in that instance of self which is then captured strongly by the resulting closure that is actually invoked.
So the type of MyClass.doSomething is (MyClass) -> () -> (), and the type of MyClass().doSomething is () -> (), where the initial MyClass reference is already captured inside the () -> ().
Given the below code:
weak var test: MyClass? = MyClass()
test?.doSomething()
On line 2, a couple things happen. First, if test is non-nil, it will be passed to the the method implementation on the type MyClass and captured by the MyClass type's curried function (MyClass) -> () ->(). This captured reference is the "self" that can be used inside that instance method.
Then, the resulting partially applied function of type ()->() is invoked, and the strong reference / capture of self from above is released once the function executes.
So, the answer is then yes, it is safe to call instance methods on a weak reference, because the instance method actually strongly captures the instance up until and while it is executing.
This post discusses how instance methods in Swift are curried in more detail, and links to the initial dev forum source of the information.
https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/
UPDATE:
Some additional interesting and in-depth articles on this mechanism:
http://rosslebeau.com/2016/sneaky-reference-cycles-swift-instance-methods
https://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/

Related

What's the difference between these two declarations of delegates?

I'm learning about delegates and I don't understand why
let friendsFunctionsDelegate = FriendsFunctionsDelegate()
let friendsFunctions = FriendsFunctions()
friendsFunctionsDelegate.delegate = friendsFunctions
is correct whilst
let friendsFunctionsDelegate = FriendsFunctionsDelegate()
friendsFunctionsDelegate.delegate = FriendsFunction()
is wrong.
Here is the full code:
protocol FriendsDelegate: AnyObject {
func orderPizza()
func takeABreak()
}
class FriendsFunctionsDelegate {
weak var delegate: FriendsDelegate? = nil
func buyPizza() {
delegate?.orderPizza()
}
func sleep() {
delegate?.takeABreak()
}
}
class FriendsFunctions: FriendsDelegate {
func orderPizza() {
print("I ordered a pizza")
}
func takeABreak() {
print("I'm going to sleep")
}
}
let friendsFunctionsDelegate = FriendsFunctionsDelegate()
let friendsFunctions = FriendsFunctions()
friendsFunctionsDelegate.delegate = friendsFunctions
ARC (for Automatic Reference Counting)
ARC is the system which manages automatically the memory allocation / deallocation for your objects.
The way it works is that as long as you'll have AT LEAST 1 strong reference to an object in your code it will remain in memory. Please note that by default any property definition not define as weak is implicitly strong.
When specifying a property as weak it won't increment the "holding reference count" by one.
The issue you are experiencing...
In your first example, when you first create a let constant you hold a strong reference to it, then you assign it to the weak variable.
You have then =>
1 weak reference to the object (stored on the friendsFunctionsDelegate.delegate)
1 strong reference to the object (the let constant let friendsFunctions = FriendsFunctions())
As a result, ARC is NOT deallocating the object (strong reference >= 1) => It is working ✅
In your second example, when you directly instantiate + assign the delegate to the weak variable WITHOUT creating a constant first.
You have then =>
1 weak reference to the object (stored on the friendsFunctionsDelegate.delegate)
0 strong reference to the object
As a result, ARC is deallocating (release from memory) the object (strong reference == 0) directly after the assignment => Not working ❌
Conclusion
As a conclusion, you need to keep a strong reference somewhere to that delegate object.
Weak usage in delegate pattern
When using delegation we use a weak property to prevent memory retain cycle. This is happening when you have one object (Object A) holding a strong reference to another object (Object B) which is also having a strong reference to the first object (Object A).
A => B STRONG
B => A STRONG
=> ⚠️RETAIN CYCLE⚠️
When you'll try to get rid of object A or B from memory strong reference count will still be 1, you'll then get a memory leak with memory filled with unused objects, which might lead to an usable app. The solution is to define one of those 2 references as weak (not both). When applying the delegate pattern, you would define the property holding the reference to the delegate as weak.
Few extra comments:
Be careful to the naming which might be misleading. The delegate keyword should be appended only to the protocol and not to the class name. If you remove it though you would have an overlap, which is a hint that the naming could have more appropriately defined.
When you define a property as Optional and you want it to be nil as default, you don't have to explicitly specify the = nil. Though you can still do it ;)
Best practices tell you that when you want a class to conform to a delegate / protocol, you should go with an extension instead of directly conforming at the class definition.
Delegate pattern should be a blind communication pattern, so your class should not be aware about out of scope functions. Then the naming of the delegate methods should be modified to something like func didBuyPizza() and func didTakeABreak()or similar.
The one with
let friendsFunctions = FriendsFunctions() // holds a strong reference
friendsFunctionsDelegate.delegate = friendsFunctions
Works as it holds a strong reference , while this
friendsFunctionsDelegate.delegate = FriendsFunction()
Don't work as both parts (lhs & rhs) are weak so no retaining happens ( delegate is a weak property )
Notice how the FriendsFunctionsDelegate.delegate property is a weak reference:
weak var delegate: FriendsDelegate? = nil
^^^^
If you did
friendsFunctionsDelegate.delegate = FriendsFunction()
You created a FriendsFunction object, and the only object that has a reference to it is friendsFunctionsDelegate, via its delegate property. But this is a weak reference. There is no strong reference to the new FriendsFunction object! As a result, it will be deallocated immediately after it is created.
You should see a warning here that says:
Instance will be immediately deallocated because property 'delegate' is 'weak'
On the other hand, if you put the newly created FriendsFunction object into a let constant first,
let friendsFunctions = FriendsFunctions()
That let constant, friendsFunctions will be a strong reference to the FriendsFunctions object, since it is not weak. And since there is at least one strong reference to it, the object will not be deallocated until all the strong references are gone.
For more info, see Automatic Reference Counting in the Swift guide.

Why does adding a closure capture list prevent my instance from being deallocated?

class Name {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("\(name) deinit")
}
}
var x: Name? = Name(name: "abc")
var someClosure = {
print("\(x?.name)")
}
someClosure()
x = nil
And then the console will output:
Optional("abc")
abc deinit
It can be seen that the "deinit" function was called. So it does not form a strong reference cycle.
But if I add a capture list to the closure:
var someClosure = { [x] in
print("\(x?.name)")
}
The console will output:
Optional("abc")
And the "deinit" function was not called. So the object and reference form a strong reference cycle.
What is the reason? What's the difference between these two conditions?
First of all, there's no strong retain cycle in either case – you just simply have a global closure variable that's holding a strong reference to your class instance, therefore preventing it from being deallocated.
In your first example, when you capture x in the closure:
var someClosure = {
print("\(x?.name)")
}
What you've got (in effect) is a reference to a reference – that is, the closure has a reference to the storage of x, which then has a reference to your class instance. When you set x to nil – the closure still has a reference to the storage of x, but now x doesn't have a reference to your class instance. Thus, your class instance no longer has any strong references to it, and can be deallocated.
In your second example when you use a capture list:
var someClosure = { [x] in
print("\(x?.name)")
}
You're copying x itself – that is, you're copying a reference to the instance of your class. Therefore the closure will keep your class retained as long as it exists. Setting x to nil doesn't affect the closure's reference to your instance, as it has it's own strong reference to it.
This is what I think happens here.
ARC adds a reference count to x when x is created. When the closure is called with a capture list, it then adds another count (closure captures the object: thus indicating to the compiler it will need x in the future, by increasing the reference count). So x now has ARC count of 2.
When you assign x to nil it reduces the reference count by 1. If the count is 0, or there are only weak references to the object, then the object is deinitialized. If the count is more than 0, and the references are strong, the object is retained.
Without explicitly passing the capture list to the closure it only weakly captures the object. ARC then decides it is safe to deinit the object once the closure is done with it. Passing the capture list indicates to ARC that the object might be needed for the closure to operate on at various multiple times, so a strong reference is made.
Put another call to someClosure() after x = nil and see what happens in both cases.

Capturing a struct reference in a closure doesn't allow mutations to occur

I am trying to see if I can use structs for my model and was trying this. When I call vm.testClosure(), it does not change the value of x and I am not sure why.
struct Model
{
var x = 10.0
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
testClosure =
{
() -> () in
model.x = 30.5
}
}
}
var vm = ViewModel(model:&m)
m.x
vm.testClosure()
m.x
An inout argument isn't a reference to a value type – it's simply a shadow copy of that value type, that is written back to the caller's value when the function returns.
What's happening in your code is that your inout variable is escaping the lifetime of the function (by being captured in a closure that is then stored) – meaning that any changes to the inout variable after the function has returned will never be reflected outside that closure.
Due to this common misconception about inout arguments, there has been a Swift Evolution proposal for only allowing inout arguments to be captured by #noescape closures. As of Swift 3, your current code will no longer compile.
If you really need to be passing around references in your code – then you should be using reference types (make your Model a class). Although I suspect that you'll probably be able to refactor your logic to avoid passing around references in the first place (however without seeing your actual code, it's impossible to advise).
(Edit: Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)
Instances of the closure will get their own, independent copy of the captured value that it, and only it, can alter. The value is captured in the time of executing the closure. Let see your slightly modified code
struct Model
{
var x = 10.0
mutating func modifyX(newValue: Double) {
let this = self
let model = m
x = newValue
// put breakpoint here
//(lldb) po model
//▿ Model
// - x : 30.0
//
//(lldb) po self
//▿ Model
// - x : 301.0
//
//(lldb) po this
//▿ Model
// - x : 30.0
}
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
model.x = 50
testClosure =
{ () -> () in
model.modifyX(301)
}
model.x = 30
}
}
let mx = m.x
vm.testClosure()
let mx2 = m.x
Here is what Apple says about that.
Classes and Structures
A value type is a type that is copied when it is assigned to a
variable or constant, or when it is passed to a function. [...] All
structures and enumerations are value types in Swift
Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or
enumeration within a particular method, you can opt in to mutating
behaviour for that method. The method can then mutate (that is,
change) its properties from within the method, and any changes that it
makes are written back to the original structure when the method ends.
The method can also assign a completely new instance to its implicit
self property, and this new instance will replace the existing one
when the method ends.
Taken from here

What does this syntax mean in Swift?

I am new to Swift and am confused with this type of syntax. I know when you add () to the end of something you initialize it. I am still confused what this means though! I am adding code below.
Please help clarify what parenthesis at the end of this means! Thank you!
Also what does it mean to have all that code after the equal sign in this case? ( I know how to create a variable and add a String,Int or something like that to it).
I am just confused a bit wth this code.
Thanks for being understanding to a beginner!
var viewController: ViewController = {
return self.instantiateViewControllerWithIdentifier("Play") as ViewController
}()
EDIT 1 -
var statusBarStyle: UIStatusBarStyle = .Default {
didSet{
setNeedsStatusBarAppearanceUpdate()
}
}
{} declares a closure, which is anonymous function. Everything between { and } is a function body. Since closure defined in provided code does not have arguments it can be executed as regular function by (). { .. }() is just defining and immediately executing of anonymous function.
In a body of a function there is a call of instantiateViewControllerWithIdentifier("Play") which returns AnyObject. As viewController variable (var) expected to ba a type of ViewController, we cast AnyObject result of instantiateViewControllerWithIdentifier as ViewController
As for statusBarStyle, UIStatusBarStyle is an enum. .Default is one of enum's cases. It can be written alternatively as var statusBarStyle = UIStatusBarStyle.Default. The code that goes in the { ... } is a way to declare getter and setter behavior. In this particular case there is only one behavior didSet defined, which means as soon as value of UIStatusBarStyle updated (which is possible, as it is var), call function setNeedsStatusBarAppearanceUpdate. There are other getters & setters keywords that you may read about in Swift Book — https://itunes.apple.com/us/book/swift-programming-language/id881256329 such as get, set, willSet.
As Nikita said its instantly calling the anonymous function you declared. This is really useful as it allows you to add logic when initialising a var or let.
Since the function takes no arguments, it makes it harder to see at first that it actually is a function. An example with an argument makes this concept a lot clearer.
let oneMore: Int = { (num: Int) in num + 1 }(5) //oneMore = 6
We are creating a function that takes one Int argument num and implicitly returns an Int (the compiler knows this because of the type annotation on oneMore. By following the closure with (5) we are calling the anonymous function with the value of 5.
Hopefully this example makes it more clear what happening. Note for an anonymous function in the context we would never need to provide argument since it will only ever be called once with the arguments following it, so we can just include the argument within the function body.
let oneMore: Int = { 5 + 1 }()
In the second example the braces are allowing you to include property observers to the variable. an example of a property observer is didSet which is called each time after you assign a value to the variable. more info can be found in apples docs here.

Class instance variable initialization order?

Currently, I am seeing something strange behavior.
class DataManager1
{
let THE_ID = "SOME_ID_STRING"
let _con1 = CKContainer(identifier: THE_ID) // error
// error: 'DataManager1.Type' does not have a member named 'THE_ID'
}
class DataManager2
{
let THE_ID = "SOME_ID_STRING"
let _con1:CKContainer?
init()
{
_con1 = CKContainer(identifier: THE_ID) // no error.
}
}
In C++ we have a defined initialization order between instance member variables. I expected something similar, but actually I couldn't find a mention for that form the manual.
Does Swift has a defined initialization order of properties? If it does, what is the rule, and where can I find the rule?
This is due to the fact that you're using a Closure (a Function is just a special case of Closure that is unnamed) to initialize the _con1 property with a default value.
From the Apple provided iBook:
If you use a closure to initialize a property, remember that the rest
of the instance has not yet been initialized at the point that the
closure is executed. This means that you cannot access any other
property values from within your closure, even if those properties
have default values. You also cannot use the implicit self property,
or call any of the instance’s methods.
Even though the note above refers specifically to closures, it seems that trying to set the default value for a property to be that of another property directly also does not work.