To avoid a memory leak when using NSBlockOperation in Objective-C, we would have to declare the variable as weak to be able to reference the block operation inside the block (to cancel if needed), typically like this:
__weak NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
if (blockOp.cancelled) {
...
}
}];
But in Swift, when I try declare my NSBlockOpeartion as weak, it is always nil.
weak var blockOp = NSBlockOperation()
Without the weak reference, all is fine except it is leaking a little bit of memory each time. How can I reference the block inside the block without leaking memory in Swift?
You can use an explicit capture list to capture an unowned reference to the operation. (This is one of the only times I'd actually suggest using unowned references, since the operation will be retained as long as its block is executing. If you're still uncomfortable with that guarantee, you could use weak instead.)
let op = NSBlockOperation()
op.addExecutionBlock { [unowned op] in
print("hi")
if op.cancelled { ... }
}
Note that this has to be split into two lines, because the variable can't be referenced from its own initial value.
Related
I ran into the weirdest thing, maybe someone has an explanation.
Steps:
Create a UIView A
Create a weak reference to A
Add A to the view hierarchy
Remove A from the view hierarchy
Set A to nil
The weak reference still exists.
If you skip step 3 and 4 the weak reference becomes nil as expected.
Code to test:
TestView to check deinit
class TestView: UIView {
deinit {
print("deinit")
}
}
Unit test
class RetainTests: XCTestCase {
func testRetainFails() {
let holderView = UIView()
var element: TestView? = TestView()
holderView.addSubview(element!)
element?.removeFromSuperview()
weak var weakElement = element
XCTAssertNotNil(weakElement)
// after next line `weakElement` should be nil
element = nil
// console will print "deinit"
XCTAssertNil(weakElement) // fails!
}
func testRetainPasses() {
var element: TestView? = TestView()
weak var weakElement = element
XCTAssertNotNil(weakElement)
// after next line `weakElement` should be nil
element = nil
// console will print "deinit"
XCTAssertNil(weakElement)
}
}
Both tests will print out deinit on the console, but in the case of the failing test the weakElement still holds the reference if element was in a view hierarchy for any time, although element got successfully deallocated. How can that be?
(Edit: This is inside an app, NOT a playground)
There's an autorelease attached to TestView when you add and remove it to the superview. If you make sure to drain the autorelease pool, then this test behaves as you expect:
autoreleasepool {
holderView.addSubview(element!)
element?.removeFromSuperview()
}
That said, you cannot rely on this behavior. There is no promise about when an object will be destroyed. You only know it will be after you release your last strong reference to it. There may be additional retains or autoreleases on the object. And there's no promise that deinit will ever be called, so definitely make sure not to put any mandatory logic there. It should only perform memory cleanup. (Both Mac and iOS, for slightly different reasons, never call deinit during program termination.)
In your failing test case,deinit is printed after the assertion fails. You can demonstrate this by placing breakpoints on the failing assertion and the print. This is because the autorelease pool is drained at the end of the test case.
The underlying lesson is that it is very common for balanced retain/autorelease calls to be be placed on an object that is passed to ObjC code (and UIKit is heavily ObjC). You should generally not expect UIKit objects to be destroyed before the end of the current event loop. Don't rely on that (it's not promised), but it's often true.
I have a class with a weak reference to its delegate. In a background operation, I need to set the delegate, perform an operation on the class, and then have the delegate released.
The code below works in Debug mode, but fails in Release mode, because in Release mode the delegate is released right away.
protocol DocumentDelegate:class { ... }
class MyDocument {
weak var delegate:DocumentDelegate?
func save() {
assert(self.delegate =! nil)
}
}
// Later:
// (#1) Using "var" does not work:
var delegate:DocumentDelegate? = InterimDelegate()
let document = MyDocument()
document.delegate = delegate
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
delegate = nil
// (#2) Using "let" does work:
let delegate:DocumentDelegate = InterimDelegate()
let document = MyDocument()
document.delegate = delegate
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
I assumed that the last instruction delegate = nil would cause the compiler to keep the delegate around until then (i.e. the "last" time the variable is used). However, thinking about it, it does make sense that the compiler optimizes the code and releases the delegate instance right away, since there are no other strong references.
However, I do not understand why the compiler does not behave the same way in the second case when using "let". Here as well the compiler could see that the delegate is not referenced via a strong reference anywhere else, but it does keep it around until the end of the block.
What would be a good way to think about this and what is a good way to keep a strong reference to the weak delegate?
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 you're describing is just a strong delegate. Get rid of weak, you don't mean it.
If you need an object to exist as long as you have a reference to it, and you plan to manually remove that reference at some point, that's a strong reference. The important thing for a delegate is that it at some point release its reference to avoid cycles. That's typically done with a weak reference because it makes things easy. But it can be done manually with strong reference. This is how URLSession's delegate works, for example.
If you intend the delegate to always be released after save, however, you may want to set it to nil in save. That can be nicer than having the caller do it (and matches how URLSession works; it automatically releases its delegate when it completes).
Just to explain what's happening in your code, ARC is allowed to release a reference after its last use.
// Example 1
// delegate is a strong reference
var delegate:DocumentDelegate? = InterimDelegate()
let document = MyDocument()
// Last read of delegate.
document.delegate = delegate
// delegate is released here. `document.delegate` is weak,
// so object is deallocated and set to nil.
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
// This assignment is a no-op. The system was allowed to set it
// to nil earlier, so this doesn't matter.
delegate = nil
// Example 2
// This creates a strong reference
let delegate:DocumentDelegate = InterimDelegate()
let document = MyDocument()
// Last read of delegate.
document.delegate = delegate
// delegate is released here. `document.delegate` is weak,
// so object is deallocated and set to nil.
// Compiled in Release mode, at this time the delegate is already nil!
document.save()
I want to make a delegate in separate class like:
class MapViewAnnotationDelegate: NSObject, SomeDelegate { //...}
and then in my controller I want to assign:
someView.delegate = MapViewAnnotationDelegate()
but the delegate is nil... how to achieve such effect? I read something about strong in Objective-C but as far as I happen to know the strong is default in Swift.
What you're doing is fine, but you need something to hold onto your instance. delegate properties by tradition are weak, so they don't ensure that the object is retained. Something else must retain it. The most common solution is to add a property to the class that owns someView, and assign your MapViewAnnotationDelegate to that property before assigning it as the delegate. That way it won't be deallocated as long as the containing objet lives. But anything that retains it is ok.
Your current code currently does this:
Create MapViewAnnotationDelegate
Assign it to someView.delegate
Note that there are no strong references to MapViewAnnotationDelegate (since someView.delegate is a weak reference)
Destroy MapViewAnnotationDelegate
Set someView.delegate to nil
One way this may look would be like this:
class Owner {
let mapViewAnnotationDelegate = MapViewAnnotationDelegate()
let someView: ...
init() {
someView.delegate = mapViewAnnotationDelegate
}
}
In this configuration, Owner (which is the owner of someView) holds onto the delegate for its lifetime.
I create a local var in a awakeFromNib function, use it inside a UIView animation block, but it is never released, why?
Here is the code (inside awakeFromNib in a UITableViewCell)
var fullPhotoDisposeBag = DisposeBag()
fullScreenImageView.rx.tapGesture().when(UIGestureRecognizerState.recognized).subscribe(onNext: { (tap) in
UIView.animate(withDuration: 0.15, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
fullScreenImageView.frame = oldFullScreenFrame
}, completion: { (finished) in
fullScreenImageView.removeFromSuperview()
let _ = fullPhotoDisposeBag //This line avoid early release, but retains too much...
})
}, onDisposed:{
print("disposed")
}).addDisposableTo(fullPhotoDisposeBag)
One clue is that the tableView is at the root of one tab of my app, so the UITableView is never deallocated thus the UITableViewCell is never deallocated due to the reusability.
But why would ARC keeps that variable? Used only in a function and a completion block?
PS: I currently use an optional DisposeBag that I set to nil in the completion block, but I don't understand why I need to do that...
Looks like a retain cycle. My guess is the garbage collector won't release the local variable until the completion closure stops referencing it, and the completion closure is never released because it is owned by your local function.
My advise would be to move away this code from your UIView to your UIViewController, since that is where this sort of behavior should be defined.
I am still trying to wrap my head around optionals in swift. Can someone explain why when we created the model object we put an '!' after and also when creating the quizCountries array of strings we also put the '!' and set it equal to nil. I don't really understand why we would want something to ever be nil in the program.
// QuizViewController.swift
import UIKit
class QuizViewController: UIViewController, ModelDelegate {
#IBOutlet weak var flagImageView: UIImageView!
#IBOutlet weak var questionNumberLabel: UILabel!
#IBOutlet weak var answerLabel: UILabel!
#IBOutlet var segmentedControls: [UISegmentedControl]!
private var model: Model! // reference to the model object
private let correctColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
private let incorrectColor = UIColor.redColor()
private var quizCountries: [String]! = nil // countries in quiz
private var enabledCountries: [String]! = nil // countries for guesses
private var correctAnswer: String! = nil
private var correctGuesses = 0
private var totalGuesses = 0
Exclamation mark after the property name indicates that this is an implicitly unwrapped optional.
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
These are useful when referenced object, e.g. model in your case, is not yet known at the moment of initialisation of QuizViewController but still is guaranteed to be available and assigned to the model property before its first use. Same goes for quizCountries, we do not know these countries yet when the controller is created but will certainly know them before they are going to be used.
In case if implicitly unwrapped optional, contrary to expectation, is (still) nil at the moment of its use the application will crash.
If you create IBOutlets by dragging from elements in interface builder to the source file they default to being Implicitly Unwrapped Optionals (IUOs) with !'s. You can manually change the them to be proper optionals with ? so that you can handle them carefully and let the compiler check all the cases.
You might be interested in the cases when they could be nil. These are a few that I have thought of although I may have missed some.
Before the view loads (before viewDidLoad has been called) they will be nil.
If you ever explicitly set them to nil.
If the item has been removed from Interface Builder.
If the outlet has been removed from Interface Builder.
If you have multiple views/view controllers in Interface Builder which use the same class and one has the outlets connected and another doesn't.
If you have two Storyboards (e.g. one for iPhone and one for iPad) that both reference the same class but only one has the IBOutlet connected.
If you ever unload the view the outlets are likely to become nil again.
Given all these possibilities I prefer to use proper optionals. I blogged about how I deal with optionals and also gave a talk on the topic.