Weak or Unowned in this example? - swift

I have a code like this:
open class WebServiceOperation: Operation {
public init(dependencies: [Operation]? = nil, finishBlock: ((_ operation: WebServiceOperation) -> Void)? = nil) {
super.init()
completionBlock = { [weak self, finishBlock] in
guard let `self` = self, self.isCancelled == false else { return }
DispatchQueue.main.sync {
finishBlock?(self)
}
}
// ...
}
}
I was wondering if I should use unowned instead of weak in completionBlock.
First - completionBlock is a block that is executed only by Operation object itself, so self inside it will not be nil. Thats why it could be unowned. But later self it's passed to finishBlock?() as a parameter. This will retain self for that finishBlock. Also this passing is done in DispatchQueue.main.sync block, which should also retain self.
So is it safe and correct to use unowned self in completionBlock instead of weak?

Related

Create weak selectors/functions in swift

I'm using RxSwift in a project and I found that when I bind directly to a selector it captures a strong reference from self and deinit wasn't called.
I was wondering how to make selector/func to deal with only a weak reference of self.
viewModel.title
.drive(onNext: updateTitle)
.disposed(by: disposeBag)
func updateTitle(_ title: String) {
navigationItem.title = title
}
What I've tried is
func updateTitle(_ title: String) {
weak var weakSelf = self
weakSelf?.navigationItem.title = title
}
But still deinit is not getting called.
Of course one solution would be to remove the function entirely
viewModel.title
.drive(onNext: { [weak self] title in
self?.updateTitle(title)
)
.disposed(by: disposeBag)
but I don't to lose the concise binding code.
Playing around with it, I found that you can get a syntax like:
viewModel.title
.drive(onNext: weakCapture(self, method: YourViewController.updateTitle))
.disposed(by: disposeBag)
The medicine is worse than the disease, but it's a bit of fun. Here's the definition of the helper method:
func weakCapture<T: AnyObject, A1>(
_ target: T,
method: #escaping (T) -> (A1) -> Void
) -> (A1) -> Void {
return { [weak target] arg in
guard let strongTarget = target else { return }
method(strongTarget)(arg)
}
}
Here's an example usage:
var c: C? = C()
let weaklyCapturedFooMethod = weakCapture(c!, method: C.foo)
weaklyCapturedFooMethod(123) // Runs foo(i: 123)
print(c as Any)
c = nil
weaklyCapturedFooMethod(123) // does nothing
It's not very nice. :P
I would recommend just using: { [weak self] self?.updateTitle($0) }
After some looking, apparently you can't make a func weak because it is not saved in the memory, on the other hand closures capture the environment around it.
So my solution was to create a closure as a var:
viewModel.title
.drive(onNext: updateTitle)
.disposed(by: disposeBag)
lazy var updateTitle: ((String) -> Void)? = { [weak self] _ in
self?.navigationItem.title = $0
}
It is a solution but still curious if we can make the selector captures weak reference.

Do I need [weak self] or [unowned self] for a Singleton in the Closure?

class Test {
private init() {}
static let shared = Test()
func test() {
}
}
let closure: ()->Void = {
Test.shared.test()
}
closure()
The code above is simple. But i want to know if I have to mark weak or unowned to the Singleton. And why?
No, because self is not used (explicitly or implicitly) in that closure.

Unowned self in a closure in a closure

If I have a closure in another closure is it enough to use unowned/weak once in the outer closure to avoid retain cycles?
Example:
foo.aClosure({[unowned self] (allowed: Bool) in
if allowed {
self.doStuff()
self.something.anotherClosure({ (s:String) -> (Void) in
self.doSomethingElse(s)
})
}
})
Only declaring weak or unowned self in the capture list of the outer closure is enough to avoid retain cycles if you don't create a strong reference to self within the outer closure (e.g. by doing: guard let strongSelf = self else { return }).
If you do create a strong reference within the closure, you must add a capture list to the inner closure to ensure that it captures your strong reference to self weakly.
Here are some examples:
import Foundation
import PlaygroundSupport
class SomeObject {
typealias OptionalOuterClosure = ((Int) -> Void)?
typealias InnerClosure = () -> Void
var outerClosure: OptionalOuterClosure
func setup() {
// Here are several examples of the outer closure that you can easily switch out below
// All of these outer closures contain inner closures that need references to self
// optionalChecks
// - has a capture list in the outer closure
// - uses the safe navigation operator (?) to ensure that self isn't nil
// this closure does NOT retain self, so you should not see the #2 calls below
let optionalChecks: OptionalOuterClosure = { [weak self] callNumber in
print("outerClosure \(callNumber)")
self?.delayCaller { [weak self] in
print("innerClosure \(callNumber)")
self?.doSomething(callNumber: callNumber)
}
}
// copiedSelfWithInnerCaptureList
// - has a capture list in the outer closure
// - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
// - has a capture list in the inner closure
// - uses the safe navigation operator (?) to ensure strongSelf isn't nil
// this closure does NOT retain self, so you should not see the #2 calls below
let copiedSelfWithInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
guard let strongSelf = self else { return }
print("outerClosure \(callNumber)")
strongSelf.delayCaller { [weak strongSelf] in
print("innerClosure \(callNumber)")
strongSelf?.doSomething(callNumber: callNumber)
}
}
// copiedSelfWithoutInnerCaptureList
// - has a capture list in the outer closure
// - creates a copy of self in the outer closure called strongSelf to ensure that self isn't nil
// - does NOT have a capture list in the inner closure and does NOT use safe navigation operator
// this closure DOES retain self, so you should see the doSomething #2 call below
let copiedSelfWithoutInnerCaptureList: OptionalOuterClosure = { [weak self] callNumber in
guard let strongSelf = self else { return }
print("outerClosure \(callNumber)")
strongSelf.delayCaller {
print("innerClosure \(callNumber)")
strongSelf.doSomething(callNumber: callNumber)
}
}
// retainingOuterClosure
// - does NOT have any capture lists
// this closure DOES retain self, so you should see the doSomething #2 call below
let retainingOuterClosure: OptionalOuterClosure = { callNumber in
print("outerClosure \(callNumber)")
self.delayCaller {
print("innerClosure \(callNumber)")
self.doSomething(callNumber: callNumber)
}
}
// Modify which outerClosure you would like to test here
outerClosure = copiedSelfWithInnerCaptureList
}
func doSomething(callNumber: Int) {
print("doSomething \(callNumber)")
}
func delayCaller(closure: #escaping InnerClosure) {
delay(seconds: 1, closure: closure)
}
deinit {
print("deinit")
}
}
// Handy delay method copied from: http://alisoftware.github.io/swift/closures/2016/07/25/closure-capture-1/
func delay(seconds: Int, closure: #escaping () -> Void) {
let time = DispatchTime.now() + .seconds(seconds)
DispatchQueue.main.asyncAfter(deadline: time) {
print("🕑")
closure()
}
}
var someObject: SomeObject? = SomeObject()
someObject?.setup()
// Keep a reference to the outer closure so we can later test if it retained someObject
let copiedOuterClosure = someObject!.outerClosure!
// Call the outer closure once just to make sure it works
copiedOuterClosure(1)
// Wait a second before we destroy someObject to give the first call a chance to work
delay(seconds: 1) {
// Run the outerClosure again to check if we retained someObject
copiedOuterClosure(2)
// Get rid of our reference to someObject before the inner closure runs
print("de-referencing someObject")
someObject = nil
}
// Keep the main run loop going so our async task can complete (need this due to how playgrounds work)
PlaygroundPage.current.needsIndefiniteExecution = true
Yes, however I would use weak over unowned because self.doStuff() with throw an exception if nil while if you use weak and its self?.doStuff() there won't be an exception thrown and it just won't execute.
You can test this in the playground with the following code:
typealias Closure = () -> Void
class ClosureObject {
var closure:Closure?
func saveClosure(closure:Closure?) {
self.closure = closure
}
}
let mainClosureObject = ClosureObject()
class TestObject {
let closureObject = ClosureObject()
func log() {
print("logged")
}
func run() {
mainClosureObject.saveClosure() {[weak self] in
self?.closureObject.saveClosure() {
self?.log()
}
}
}
}
var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()
and compare it with:
typealias Closure = () -> Void
class ClosureObject {
var closure:Closure?
func saveClosure(closure:Closure?) {
self.closure = closure
}
}
let mainClosureObject = ClosureObject()
class TestObject {
let closureObject = ClosureObject()
func log() {
print("logged")
}
func run() {
mainClosureObject.saveClosure() {
self.closureObject.saveClosure() {
self.log()
}
}
}
}
var testObject:TestObject? = TestObject()
let closureObject = testObject?.closureObject
testObject?.run()
mainClosureObject.closure?()
closureObject?.closure?()
testObject = nil
closureObject?.closure?()
mainClosureObject.closure?()
closureObject?.closure?()

Swift using function parameter inside closure

I just want to use function parameter inside swift closure without memory leak, so I just want to confirm if I do in following way will there be any memory related issues? Kindly let me know
func someMethod(someValue: String) {
weak var weakSelf = self
var copyOfSomeValue: String? = someValue.copy() as? String
self.someOtherMethodWithCompletion(completionHandler: { () -> Void in
if let strongSelf = weakSelf, let originalValue = copyOfSomeValue {
strongSelf.updateMyViewWithText(originalValue)
}
})
}
String is a value type. Even though closures capture them by reference, there is no need to make a copy unless you are going to change someValue after the capture. Even then you're better off using capture lists [someValue], which are also used when you need to declare [weak self].
Using weak or unowned is situational, read about them here.
func someMethod(someValue: String) {
someOtherMethodWithCompletion { [weak self] in
self?.updateMyViewWithText(someValue)
}
}
With swift you should use [unowned self] and just use swift as normal, for example:
func someMethod(someValue: String) {
var copyOfSomeValue: String? = someValue.copy() as? String
self.someOtherMethodWithCompletion(completionHandler: { () ->
[unowned self]
in
if let originalValue = copyOfSomeValue {
self.updateMyViewWithText(originalValue)
}
})
}
You should use weak or unowned just to variables which can cause reference cycle. The difference between those is:
Use a weak reference whenever it is valid for that reference to become
nil at some point during its lifetime. Conversely, use an unowned
reference when you know that the reference will never be nil once it
has been set during initialization

Swift weak self function retention

If I have a closure that references a weak var weakSelf = self, can I change that closure to a direct function reference, through weakSelf?
struct ClosureHolder {
let closure: () -> Void
}
class ClosureSource {
func hello() {
NSLog("hello")
}
func createWeakSelfWithinInnerClosureClosureHolder() -> ClosureHolder {
weak var weakSelf = self
return ClosureHolder(closure: {
weakSelf?.hello()
})
}
func createWeakSelfDotHelloClosureHolder() -> ClosureHolder {
weak var weakSelf = self
// The code below won't compile because weakSelf is an Optional.
// Once I unwrap the optional, I no longer have a weak reference.
// return ClosureHolder(closure: weakSelf.hello)
// this strongifies the weak reference. :(
return ClosureHolder(closure: weakSelf!.hello)
}
}
Instead of createWeakSelfWithinInnerClosureClosureHolder, I'd prefer something like createWeakSelfDotHelloClosureHolder.
No you can't. Saying self.foo (if foo is a method) is exactly the same thing as to saying MyClass.foo(self). And methods curried in this fashion always keep a strong reference to the receiver object. If you want to maintain a weak reference, then you need to stick with the { weakSelf?.hello() } approach.