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

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.

Related

How can I Intentionally create a Zombie Object in Swift [duplicate]

I've read How to demonstrate memory leak and zombie objects in Xcode Instruments? but that's for objective-c. The steps don't apply.
From reading here I've understood zombies are objects which are:
deallocated
but something pointer is still trying to point to them and send messages to them.
not exactly sure how that's different from accessing a deallocated object.
I mean in Swift you can do:
var person : Person? = Person(name: "John")
person = nil
print(person!.name)
Is person deallocated? Yes!
Are we trying to point to it? Yes!
So can someone share the most common mistake which leads to creating a dangling pointer?
Here's zombie attack in under 15 lines of code:
class Parent { }
class Child {
unowned var parent: Parent // every child needs a parent
init(parent: Parent) {
self.parent = parent
}
}
var parent: Parent? = Parent()
let child = Child(parent: parent!) // let's pretend the forced unwrap didn't happen
parent = nil // let's deallocate this bad parent
print(child.parent) // BOOM!!!, crash
What happens in this code is that Child holds an unowned reference to Parent, which becomes invalid once Parent gets deallocated. The reference holds a pointer to the no longer living parent (RIP), and accessing that causes a crash with a message similar to this:
Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated2018-10-29 20:18:39.423114+0200 MyApp[35825:611433] Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated
Note The code won't work in a Playground, you need a regular app for this.
This is not a dangling pointer or a zombie. When you use ! you're saying "if this is nil, then crash." You should not think of person as a pointer in Swift. It's a value. That value may be .some(T) or it may be .none (also called nil). Neither of those is dangling. They're just two different explicit values. Swift's nil is nothing like null pointers in other languages. It only crashes like null pointers when you explicitly ask it to.
To create zombies, you'll need to be using something like Unmanaged. This is extremely uncommon in Swift.
Zombie objects are Objective-C objects which have been deallocated, but still receive messages.
In Objective-C, __unsafe_unretained can be used to create an additional pointer to an object which does not increase the reference count. In Swift that would be unowned(unsafe).
Here is a self-contained example:
import Foundation
class MyClass: NSObject {
func foo() { print("foo"); }
deinit { print("deinit") }
}
unowned(unsafe) let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = obj1
print("exit scope")
}
obj2.foo()
obj1 is a pointer to a newly created object, and obj2 is another pointer to the same object, but without increasing the reference counter. When the do { ... } block is left, the object is deallocated. Sending a message to it via obj2 causes the Zombie error:
exit scope
deinit
*** -[ZombieTest.MyClass retain]: message sent to deallocated instance 0x1005748d0
This can also happen if you use Managed (e.g. to convert pointers to Swift objects to C void pointers in order to pass them to C callback functions) and you don't choose the correct retained/unretained combination. A contrived example is:
let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = Unmanaged.passUnretained(obj1).takeRetainedValue()
print("exit scope")
}
obj2.foo()
Zombie Object is a deallocated object which behaves itself like alive. It is possible because of dangling pointer.
dangling pointer points on some address in the memory where data is unpredictable
Objective-C has unsafe_unretained[About] property attributes. [Example]
Swift has
unowned(unsafe)[About] reference
Unmanaged - a wrapper for non ARC Objc- code
unowned(unsafe) let unsafeA: A
func main() {
let a = A() // A(ref count = 1)
unsafeA = a
} // A(ref count = 0), deinit() is called
func additional() {
unsafeA.foo() //<- unexpected
}

How can I demonstrate a zombie object in Swift?

I've read How to demonstrate memory leak and zombie objects in Xcode Instruments? but that's for objective-c. The steps don't apply.
From reading here I've understood zombies are objects which are:
deallocated
but something pointer is still trying to point to them and send messages to them.
not exactly sure how that's different from accessing a deallocated object.
I mean in Swift you can do:
var person : Person? = Person(name: "John")
person = nil
print(person!.name)
Is person deallocated? Yes!
Are we trying to point to it? Yes!
So can someone share the most common mistake which leads to creating a dangling pointer?
Here's zombie attack in under 15 lines of code:
class Parent { }
class Child {
unowned var parent: Parent // every child needs a parent
init(parent: Parent) {
self.parent = parent
}
}
var parent: Parent? = Parent()
let child = Child(parent: parent!) // let's pretend the forced unwrap didn't happen
parent = nil // let's deallocate this bad parent
print(child.parent) // BOOM!!!, crash
What happens in this code is that Child holds an unowned reference to Parent, which becomes invalid once Parent gets deallocated. The reference holds a pointer to the no longer living parent (RIP), and accessing that causes a crash with a message similar to this:
Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated2018-10-29 20:18:39.423114+0200 MyApp[35825:611433] Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated
Note The code won't work in a Playground, you need a regular app for this.
This is not a dangling pointer or a zombie. When you use ! you're saying "if this is nil, then crash." You should not think of person as a pointer in Swift. It's a value. That value may be .some(T) or it may be .none (also called nil). Neither of those is dangling. They're just two different explicit values. Swift's nil is nothing like null pointers in other languages. It only crashes like null pointers when you explicitly ask it to.
To create zombies, you'll need to be using something like Unmanaged. This is extremely uncommon in Swift.
Zombie objects are Objective-C objects which have been deallocated, but still receive messages.
In Objective-C, __unsafe_unretained can be used to create an additional pointer to an object which does not increase the reference count. In Swift that would be unowned(unsafe).
Here is a self-contained example:
import Foundation
class MyClass: NSObject {
func foo() { print("foo"); }
deinit { print("deinit") }
}
unowned(unsafe) let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = obj1
print("exit scope")
}
obj2.foo()
obj1 is a pointer to a newly created object, and obj2 is another pointer to the same object, but without increasing the reference counter. When the do { ... } block is left, the object is deallocated. Sending a message to it via obj2 causes the Zombie error:
exit scope
deinit
*** -[ZombieTest.MyClass retain]: message sent to deallocated instance 0x1005748d0
This can also happen if you use Managed (e.g. to convert pointers to Swift objects to C void pointers in order to pass them to C callback functions) and you don't choose the correct retained/unretained combination. A contrived example is:
let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = Unmanaged.passUnretained(obj1).takeRetainedValue()
print("exit scope")
}
obj2.foo()
Zombie Object is a deallocated object which behaves itself like alive. It is possible because of dangling pointer.
dangling pointer points on some address in the memory where data is unpredictable
Objective-C has unsafe_unretained[About] property attributes. [Example]
Swift has
unowned(unsafe)[About] reference
Unmanaged - a wrapper for non ARC Objc- code
unowned(unsafe) let unsafeA: A
func main() {
let a = A() // A(ref count = 1)
unsafeA = a
} // A(ref count = 0), deinit() is called
func additional() {
unsafeA.foo() //<- unexpected
}

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

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/

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.

How do I use objective-c-runtime's object_getIvar & object_setIvar in swift?

Does anybody know why I get BAD_ACCESS on getting & setting of my iVars with the following code ?
class myClass: NSObject {
var model = "Unspecified"
override init() {
super.init()
var key: NSString = "model"
var aClass : AnyClass? = self
var ivar: Ivar = class_getInstanceVariable(aClass, key.UTF8String)
// Set
object_setIvar(aClass, ivar, "R56")
// Get
var value: AnyObject = object_getIvar(aClass, ivar)
}
}
myClass()
You get a bad access because Swift classes do not have traditional iVars anymore (try to grab a Swift class' iVar layout and see for yourself). But Swift classes are also Objective-C objects to the runtime, and there don't appear to be any paths excluding Swift classes in it.
What winds up happening is the runtime hands you a bogus iVar that it truly thinks points to a proper offset in the class definition (probably 0 or thereabouts because it doesn't exist). When you try to get said iVar, the runtime literally derefs you some of the object's bytes and tries to wrap it back up in an Objective-C pointer. Often this data has the tagged bit set unintentionally, and so often maps to tagged classes (in my tests I was reliably getting back a tagged pointer for what the runtime thought was NSMutableData).
Long story short: you can't do it anymore.