Swift closure is still in memory after VC deinit is called - ios

I have a bluetooth class which passes when a char value is updated to a closure in a view controller (as well as the same closure in a singleton class). when the VC deinit is called, the closure in the VC is still being executed when the char value is updated. I am using [weak self] for the closure in the VC. I'd like to be able to stop this VC closure from being called when the view is deinitialised. But I also don't understand why the other callback in the singleton is not being executed after the VC is presented!
Included below is the syntax for the closure inside the VC
bluetooth.updatedCharacteristicsValue { [weak self] char in

[weak self] does not mean that the closure can be discarded, it only prevents the closure from retaining the VC (and therefore preventing the VC from being deinited).
Simply begin your closure with:
guard let self = self else { return }
... to exit early if the VC no longer exists.
As for why the closure supplied by the VC is being called but the one in the singleton isn't, it sounds like your bluetooth class doesn't understand the concept of multiple 'users'. Whoever registers their callback last is the one that is called.
An approach to handling your own observer registration with convenient self-unregistering tokens:
class ObserverToken {
let id = UUID()
private let onDeinit: (UUID) -> ()
init(onDeinit: #escaping (UUID) -> ()) {
self.onDeinit = onDeinit
}
deinit {
onDeinit(id)
}
}
class BluetoothThing {
// Associate observers with the .id of the corresponding token
private var observers = [UUID: (Int) -> ()]()
func addObserver(using closure: #escaping (Int) -> ()) -> ObserverToken {
// Create a token which sets the corresponding observer to nil
// when it is deinit'd
let token = ObserverToken { [weak self] in self?.observers[$0] = nil }
observers[token.id] = closure
return token
}
func tellObserversThatSomethingHappened(newValue: Int) {
// However many observers we currently have, tell them all
observers.values.forEach { $0(newValue) }
}
deinit {
print("👋")
}
}
// I've only made this var optional so that it can later be set to nil
// to prove there's no retain cycle with the tokens
var bluetooth: BluetoothThing? = BluetoothThing()
// For as long as this token exists, updates will cause this closure
// to be called. As soon as this token is set to nil, it's deinit
// will automatically deregister the closure
var observerA: ObserverToken? = bluetooth?.addObserver { newValue in
print("Observer A saw: \(newValue)")
}
// Results in:
// Observer A saw: 42
bluetooth?.tellObserversThatSomethingHappened(newValue: 42)
// A second observer
var observerB: ObserverToken? = bluetooth?.addObserver { newValue in
print("Observer B saw: \(newValue)")
}
// Results in:
// Observer A saw: 123
// Observer B saw: 123
bluetooth?.tellObserversThatSomethingHappened(newValue: 123)
// The first observer goes away.
observerA = nil
// Results in:
// Observer B saw: 99
bluetooth?.tellObserversThatSomethingHappened(newValue: 99)
// There is still one 'live' token. If it is retaining the
// Bluetooth object then this assignment won't allow the
// Bluetooth to deinit (no wavey hand)
bluetooth = nil
So if your VC stores it's token as a property, when the VC goes away, the token goes away and the closure is deregistered.

Related

Should we continue explicitly capturing variable as weak in iOS

Suppose we got a chain of closures, like so:
var myOtherVc: UIViewController! // get it somehow
self.dismiss(animated: true, completion: { [weak myOtherVc] in
myOtherVc?.present(sthElse, animated: true, completion: { [weak myOtherVc] in // <-- HERE
})
})
My question is if we captured variable myOtherVc in the topmost block as weak should we keep being explicit about weak in all the children blocks or is compiler smart enough to tell ARC to not retain?
Update
I guess I need to clarify, what if the block was escaping?
Also, i DO care about delayed deallocation. This is the whole point of using weak for me.
public func doWhatever(_ success: #escaping () -> Void) {
// do whatever
})
var myOtherVc: UIViewController! // get it somehow
self.dismiss(animated: true, completion: { [weak myOtherVc] in
SomeClass.doWhatever({ [weak myOtherVc] in // <-- HERE
myOtherVc?.present(sthElse, animated: true, completion: { [weak myOtherVc] in // <-- and HERE, too
})
})
})
We suppose all closures are escaping, so I made this playground and I conclude that you must capture your variable as weak in the latest closure that is using your variable and it won't infer its reference type from the parent or top closure:
typealias Closure = () -> Void
class A {
var closureA : Closure?
func runClosureA(closure: #escaping Closure) {
self.closureA = closure
closureA?()
}
func print() {
debugPrint("A is here!")
}
deinit {
debugPrint("A deinited")
}
}
another class which operates on chained closure:
class B {
func runClosureB(closure: #escaping Closure) {
closure()
}
func operate() {
let a : A = A()
runClosureB { [weak a] in
a?.runClosureA { [a] in
a?.print()
}
}
}
deinit {
debugPrint("B deinited")
}
}
The code is:
var b: B? = B()
b?.operate()
b = nil
It will prints:
// "A is here!"
// "B deinited"
But by changing the operate function to this:
func operate() {
let a : A = A()
runClosureB { [a] in
a.runClosureA { [weak a] in
a?.print()
}
}
}
The result will change to this:
"A is here!"
"A deinited"
"B deinited"
Update: In class A I made a strong reference to the closure as closureA, if you don't create the reference, there is no need to capture self as weak in closures.
In fact, it depends on which closure you are using and the relations between them and if there can be retained cycle so you should consider capturing the right closure as weak.
In your case, with present(_, animated:, completion:), the completion block is non-escaping so if you want to use weak reference you can use it but it is not necessary to use.
Non-escaping closures do not require [weak self] unless you care about delayed deallocation
Please check the article about weak, unowned references in nested closures.
I made a small test in the playground that shows me that the compiler is indeed quite smart and tells ARC not to retain.
class WeakThingy {
var variable: Int
init(variable: Int) {
self.variable = variable
}
deinit {
print("deinit WeakThingy")
}
}
class ClosureTest {
var maybeNil: WeakThingy
init(maybeNil: WeakThingy) {
self.maybeNil = maybeNil
}
deinit {
print("deinit ClosureTest")
}
func bien() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak maybeNil] in
print("first \(String(describing: maybeNil))")
maybeNil?.variable = 12
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
print("second \(String(describing: maybeNil))")
maybeNil?.variable = 12
}
}
}
}
var closureTest:ClosureTest? = ClosureTest(maybeNil: WeakThingy(variable: 12))
closureTest?.bien()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
closureTest = nil
}
What's printed is the following
first Optional(__lldb_expr_34.WeakThingy)
deinit ClosureTest
deinit WeakThingy
second nil
What happen here is that I pass maybeNil in the first closure as weak, but not in the second. Then I make it nil before the second closure gets executed. We can see in the output that it is well deallocated before entering the second closure.

ARC Retain Cycles, Grand Central Dispatch and Closures

I understand that if you assign a closure to a property such as:
var someClosure : () -> Void?
let someConstant : Int = 5
And call that closure from its class' init with a reference to self:
someClosure = { print(self.someConstant) }
Then this will result in a retain cycle, so I should use weak to avoid it.
But I couldn't understand the following code which is from a raywenderlich.com tutorial:
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else {
return
}
self.someFunction()
}
Why a retain cycle exists here - since we didn't create a reference from view to closure (i.e., saving closure as a property) but only from closure to self; shouldn't it be retain cycle-free?
You are right and Ray is wrong:
Grand Central Dispatch closures don't cause retain cycles.
You can delete
[weak self] in
guard let self = self else {
return
}
Please see also https://digitalleaves.com/blog/2015/05/demystifying-retain-cycles-in-arc/

Holding a Strong ref to local object that uses closure [Swift]

I have a question regarding holding a Strong ref to local object that uses closure.
I have the following code, which object B uses a method with closure of a local object of type A.
The method in object A, uses async action to perform some network task and then return the closure to object b.
Since object A is local in a method in B and since i'm using [weak self] in the the object A async task (to prevent retain cycle), the object gets released.
What should i change in the following code in order to ensure the local A object will get released only when the closure is done?
This is part of the important code:
class A {
var restAPI: RestAPI?
func fetchNews(completion: (_ json: [String:Any])->()) {
// .....
self.restAPI.fetch(url: url, results: { [weak self] (json) in //
completion(json)
})
// .....
}
}
class B {
// ....
// ... call to updateNews()
func updateNews() {
let aFetcher: A()
aFetcher.fetchNews(completion : {
// <<<< // aFetcher gets released and closue never called
// parse...
}
}
}
You declare aFetcher as a let in the scope of func updateNews()
When the scope of updateNews() reaches its end aFetcher will be released.
Your have [weak self] in your internal fetch function.
On this stage aFetcher will be released because updateNews() finish its executing and there are no strong references to this object.
You just need to add variable aFetcher to class B to ensure you have strong reference to aFetcher.
class B {
// MARK: - Vars
private let aFetcher = A()
// MARK: - Public
func updateNews() {
aFetcher.fetchNews(completion : {
// parse...
}
}
}
You need a strong reference on the top level of the class.
However not to keep the reference permanently and to retain and release it reliably add a optional stored property in class B and set it to nil in the completion closure:
class B {
var fetcher : A?
// MARK: - Public
func updateNews() {
fetcher = A()
fetcher!.fetchNews(completion : { [unowned self] in
// parse...
self.fetcher = nil
}
}
}

set properties on rectangle request. iOS, Swift

Im trying to set properties on a rectangle request.
Im trying to set minimum size, aspect ratio and others but get the error below.
Fatal error: Attempted to read an unowned reference but the object was already deallocated2018-08-13 16:08:09.081049+0100 app[4000:1277980] Fatal error: Attempted to read an unowned reference but the object was already deallocated
func performVisionRequest(image: CGImage, orientation: CGImagePropertyOrientation) {
DispatchQueue.global(qos: .userInitiated).async {
do {
let imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:])
try imageRequestHandler.perform(
[VNDetectRectanglesRequest(completionHandler:{ req, err in
self.rectanglesRequest(request:req, error:err)
})]
)
} catch let error as NSError {
self.sliceCompletion([UIImage]())
print("Failed to perform vision request: \(error)")
}
}
}
func rectanglesRequest(request: VNRequest, error: Error?) {
if let err = error as NSError? {
noRect = true
var slices = [imageNo]
self.sliceCompletion(slices as! [UIImage])
slices = []
print("Failed during detection: \(err.localizedDescription)")
return
}
unowned let rectanglesRequestSet = VNDetectRectanglesRequest()
rectanglesRequestSet.minimumSize = 0.07
rectanglesRequestSet.minimumAspectRatio = 0.2
rectanglesRequestSet.maximumAspectRatio = 0.3
rectanglesRequestSet.quadratureTolerance = 22.0
Here it is not good to mark rectanglesRequestSet as unowned, as it is causing it to be released before you can use it and when you attempt to message a released unowned object you will get a crash, which is explains the message you get:
Attempted to read an unowned reference but the object was already deallocated
unowned is used to break retain cycles which occur when a class's property holds a reference back to class itself, often through a closure. For example:
class ViewControllerA: UIViewController {
let viewControllerB = UIViewControllerB()
override func viewDidLoad() {
super.viewDidLoad()
viewControllerB.onSelect = {
self.onSelection()
}
}
func onSelection() {
// do something
}
}
class ViewControllerB: UIViewController {
var onSelect: (() -> Void)?
}
For example the code above creates a retain cycle as vcA -> vcB -> onSelect -> vcA, where vcA is an instance of ViewControllerA and vcB is an instance of ViewControllerB. vcA and vcB will never be released as vcA holds a reference to vcB by virtue of it being a property. And, vcB holds a reference to vcA by virtue of variable capture in the onSelect closure. This happens because in order for closures to execute code in the future they must hold a reference to all of the objects used in the closure, in the example vcA is the only object used so the closure holds a reference to it and vcB holds a reference to the closure. To prevent this retain cycle:
viewControllerB.onSelect = { [unowned self]
self.onSelection()
}
or
viewControllerB.onSelect = { [weak self]
self?.onSelection()
}
will cause the closure to not capture vcA meaning there will be no retain cycle. It is safer to use weak rather than unowned. The closure can't guarantee that un-captured objects will be around at execution weak will allow such objects to be nil, unowned is, in a manner, stipulating that it won't be nil and instructing the program to crash if it is.

Using 'self' on RxSwift closures... What about instance methods as param?

In other stack overflow questions, it was emphasized that the capture [weak self] should be used for closures that aren't owned by the class because self could be nil before the closure completes. An alternative when the closure is owned by the class itself is [unowned self].
My question is do I need to use [unowned self] when the function I pass as a parameter is an instance method of the current class?
Example
import RxSwift
class Person {
var name = "Default name"
class func getPersons() -> Observable<Person> {
// ...
}
}
class MyController: UIViewController {
let disposeBag = DisposeBag()
// I know this right
func unownedDisplayPeople() {
Person.getPersons()
.subscribeNext { [unowned self ] person in
self.displayName(person)
}
.addDisposableToBag(disposeBag)
}
// But what about this?
func whatAboutThisDisplayPeople() {
Person.getPersons()
.subscribeNext(displayName)
.addDisposableToBag(disposeBag)
}
// Or this?
func orThisDisplayPeople() {
Person.getPersons()
.subscribeNext(self.displayName)
.addDisposableToBag(disposeBag)
}
func displayName(person: Person) {
print("Person name is \(person.name)")
}
}
If I still need to think about the reference counting when I just pass an instance method, how do I do it? Where do i put the [unowned self]? Or is it considered [unowned self] already when I just pass the instance method?
Unfortunately, passing an instance method to subscribeNext will retain self. To be more generic, storing a reference to an instance method will increase the retain count of the instance.
let instance = ReferenceType()
print(CFGetRetainCount(instance)) // 1
let methodReference = instance.method
print(CFGetRetainCount(instance)) // 2
The only solution here is do what you have done in unownedDisplayPeople.
let instance = ReferenceType()
print(CFGetRetainCount(instance)) // 1
let methodReference = { [unowned instance] in instance.method() }
print(CFGetRetainCount(instance)) // 1

Resources