Classes, both in Swift and in Objective-C, are reference types (see “Value Types and Reference Types”). Behind the scenes, Swift and Objective-C memory management for reference types works essentially the same way. Such memory management, as I pointed out in Chapter 5, can be a tricky business.
Fortunately, Swift uses ARC (automatic reference counting), so you don’t have to manage the memory for every reference type object explicitly and individually, as was once necessary in Objective-C. Thanks to ARC, you are far less likely to make a memory management mistake, and more of your time is liberated to concentrate on what your app actually does instead of dealing with memory management concerns.
Still, even in Swift, and even with ARC, it is possible to make a memory management mistake, or to be caught unawares by Cocoa’s memory management behavior. A memory management mistake can lead to runaway excessive memory usage, crashes, or mysterious misbehavior of your app. Cocoa memory management can be surprising in individual cases, and can mislead you into making a memory management mistake; so you need to understand, and prepare for, what Cocoa is going to do.
The reason why reference type memory must be managed at all is that references to reference type objects are merely pointers to the actual object, and there can be multiple references (pointers) to the very same object. This means that every reference must deal carefully with that object, out of consideration for the needs of other possible references. At the very latest, the object should go out of existence when there are no pointers to it. But so long as there are any pointers to it, the object must not go out of existence.
To illustrate, imagine three objects, Manny, Moe, and Jack, where both Manny and Moe have references to Jack. Jack is the object whose memory we are concerned to manage:
If both Manny and Moe go out of existence, and if no other object has a reference to Jack, Jack should go out of existence too. An object without a pointer to it is useless; it is occupying memory, but no other object has, or can ever get, a reference to it. This is a memory leak.
If both Manny and Moe have a pointer to Jack, and if Manny somehow causes Jack to go out of existence, poor old Moe is left with a pointer to nothing (or worse, to garbage). A pointer whose object has been destroyed behind the pointer’s back is a dangling pointer. If Moe subsequently uses that dangling pointer to send a message to the object that he thinks is there, the app will crash.
To prevent both memory leakage and dangling pointers, there is a policy of manual memory management based on a number, maintained by every reference type object, called its retain count. The rule is that other objects can increment or decrement an object’s retain count — and that’s all they are allowed to do. As long as an object’s retain count is positive, the object will persist. No object has the direct power to tell another object to be destroyed; rather, as soon as an object’s retain count is decremented to zero, it is destroyed automatically.
By this policy, every object that needs Jack to persist should increment Jack’s retain count, and should decrement it once again when it no longer needs Jack to persist. As long as all objects are well-behaved in accordance with this policy, the problem of manual memory management is effectively solved:
There cannot be any dangling pointers, because any object that has a pointer to Jack has incremented Jack’s retain count, ensuring that Jack persists.
There cannot be any memory leaks, because any object that no longer needs Jack decrements Jack’s retain count. If every object that doesn’t need Jack any longer behaves this way, then when no object needs Jack any longer, Jack’s retain count will reach zero and Jack will go out of existence.
An object is well-behaved with respect to memory management as long as it adheres to certain very simple, well-defined rules in conformity with the basic concepts of memory management. The underlying ethic is that each object that has a reference to a reference type object is responsible solely for its own memory management of that object, in accordance with these rules. If all objects that ever get a reference to this reference type object behave correctly with respect to these rules, the object’s memory will be managed correctly and it will go out of existence exactly when it is no longer needed:
If Manny or Moe explicitly instantiates Jack — by directly calling an initializer — then the initializer increments Jack’s retain count.
If Manny or Moe makes a copy of Jack — by calling copy
or mutableCopy
or any other method with copy
in its name — then the copy method increments the retain count of this new, duplicate Jack.
If Manny or Moe acquires a reference to Jack (not through explicit instantiation or copying), and needs Jack to persist — long enough to work with Jack in code, or long enough to be the value of an instance property — then he himself increments Jack’s retain count. (This is called retaining Jack.)
If and only if Manny or Moe has done any of those things — that is, if Manny or Moe has ever directly or indirectly caused Jack’s retain count to be incremented — then when he himself no longer needs his reference to Jack, before letting go of that reference, he decrements Jack’s retain count to balance exactly all previous increments that he himself has performed. (This is called releasing Jack.) Having released Jack, Manny or Moe should then assume that Jack no longer exists, because if this causes Jack’s retain count to drop to zero, Jack will no longer exist. This is the golden rule of memory management — the rule that makes memory management work coherently and correctly.
A general way to understand the golden rule of memory management is in terms of ownership. If Manny has created, copied, or retained Jack — that is, if Manny has ever incremented Jack’s retain count — Manny has asserted ownership of Jack. Both Manny and Moe can own Jack at the same time, but each is responsible only for managing his own ownership of Jack correctly. It is the responsibility of an owner of Jack eventually to decrement Jack’s retain count — to release Jack, resigning ownership of Jack. The owner says: “Jack may or may not persist after this, but as for me, I’m done with Jack, and Jack can go out of existence as far as I’m concerned.” At the same time, a nonowner of Jack must never release Jack. As long as all objects behave this way with respect to Jack, Jack will not leak nor will any pointer to Jack be left dangling.
Once upon a time, retaining and releasing an object was a matter of you, the programmer, literally sending retain
and release
messages to it. NSObject still implements retain
and release
, but under ARC (and in Swift) you can’t call them. That’s because ARC is calling them for you! That’s ARC’s job — to do for you what you would have had to do if memory management were still up to the programmer.
ARC is implemented as part of the compiler. The compiler is literally modifying your code by inserting retain
and release
calls behind the scenes. When you receive a reference type object by calling some method, ARC immediately retains it so that it will persist for as long as this same code continues to run; then ARC releases it when the code comes to an end. Similarly, when you create or copy a reference type object, ARC knows that its retain count has been incremented, and releases it when the code comes to an end.
ARC is very conservative, but also very accurate. In effect, ARC retains at every juncture that might have the slightest implications for memory management: it retains when an object is received as an argument, it retains when an object is assigned to a variable, and so forth. It may even insert temporary variables, behind the scenes, to enable it to refer sufficiently early to an object so that it can retain it. But of course it eventually also releases to match.
Built-in Cocoa objects will take ownership of objects that you hand to them, by retaining them, if it makes sense for them to do so, and will of course then balance that retain with a release later. Indeed, this is so generally true that if a Cocoa object is not going to retain an object you hand it, there will be a note to that effect in the documentation.
A collection, such as an array or dictionary, is a particularly obvious case in point. An object can hardly be an element of a collection if that object can go out of existence at any time; so when you add an element to a collection, the collection asserts ownership of the object by retaining it. Thereafter, the collection acts as a well-behaved owner. If this is a mutable collection, then if an element is removed from it, the collection releases that element. If the collection object goes out of existence, it releases all its elements.
Prior to ARC, removing an object from a mutable collection constituted a potential trap. Consider the following Objective-C code:
id obj = myMutableArray[0]; // an NSMutableArray [myMutableArray removeObjectAtIndex: 0]; // bad idea in non-ARC code! // ... could crash here by referring to obj ...
As I just said, when you remove an object from a mutable collection, the collection releases it. So, without ARC, the second line of that code involves an implicit release of the object that used to be the first element of myMutableArray
. If this reduces the object’s retain count to zero, it will be destroyed. The pointer obj
will then be a dangling pointer, and a crash may be in our future when we try to use it as if it were a real object.
With ARC, however, that sort of danger doesn’t exist. Assigning a reference type object to a variable retains it! But we did assign this object to a variable, obj
, before we removed it from the collection. Therefore, that code is perfectly safe, and so is its Swift equivalent:
let obj = myMutableArray[0] // retain myMutableArray.removeObject(at:0) // release // ... safe to refer to obj ...
The first line retains the object. The second line releases the object, but that release balances the retain that was placed on the object when the object was placed in the collection originally. The object’s retain count is still more than zero, and it continues to exist for the duration of this code.
When a method creates an instance and returns that instance, some memory management hanky-panky has to take place. Consider this simple code:
func makeImage() -> UIImage? { if let im = UIImage(named:"myImage") { return im } return nil }
Think about the retain count of im
, the UIImage we are returning. This retain count has been incremented by our call to the UIImage initializer UIImage(named:)
. According to the golden rule of memory management, as we pass im
out of our own control by returning it, we should decrement the retain count of im
, balancing the increment and surrendering ownership. But when can we possibly do that? If we do it before the line return im
, the retain count of im
will be zero and it will vanish in a puff of smoke; we will be returning a dangling pointer. But we can’t do it after the line return im
, because when that line is executed, our code comes to an end.
Clearly, we need a way to vend this object without decrementing its retain count now — so that it stays in existence long enough for the caller to receive and work with it — while ensuring that at some future time we will decrement its retain count, so as to balance our init(named:)
call and fulfill our own management of this object’s memory. The solution is something midway between releasing the object and not releasing it — autoreleasing it.
Here’s how autoreleasing works. Your code runs in the presence of something called an autorelease pool. When ARC autoreleases an object, that object is placed in the autorelease pool, and a number is incremented saying how many times this object has been placed in this autorelease pool. From time to time, when nothing else is going on, the autorelease pool is automatically drained. This means that the autorelease pool releases each of its objects, the same number of times as that object was placed in this autorelease pool, and empties itself of all objects. If that causes an object’s retain count to be zero, so be it; the object is destroyed in the usual way. So autoreleasing an object is just like releasing it, but with a proviso, “later, not right this second.”
In general, autoreleasing and the autorelease pool are merely an implementation detail. You can’t see them; they are just part of how ARC works. But sometimes, on very rare occasions, you might want to drain the autorelease pool yourself. Consider the following code (it’s slightly artificial, but that’s because demonstrating the need to drain the autorelease pool isn’t easy):
func test() { let path = Bundle.main.path(forResource:"001", ofType: "png")! for j in 0 ..< 50 { for i in 0 ..< 100 { let im = UIImage(contentsOfFile: path) } } }
That method does something that looks utterly innocuous; it loads an image. But it loads it repeatedly in a loop. As the loop runs, memory climbs constantly (Figure 12-1); by the time our method comes to an end, our app’s memory usage has reached almost 34MB. This is not because the images aren’t being released each time through the loop; it’s because a lot of intermediate objects — things you’ve never even heard of, such as NSPathStore2 objects — are secondarily generated by our call to init(contentsOfFile:)
and are autoreleased. As we keep looping, those objects are all sitting there, piling up in the autorelease pool by the tens of thousands, waiting for the pool to be drained. When our code finally comes to an end, the autorelease pool is drained, and our memory usage drops precipitately back down to almost nothing.
Granted, 34MB isn’t exactly a massive amount of memory. But you may imagine that a more elaborate inner loop might generate more and larger autoreleased objects, and that our memory usage could potentially rise quite significantly. It would be nice to have a way to drain the autorelease pool manually now and then during the course of a loop with many iterations. Swift provides such a way — the global autoreleasepool
function, which takes a single argument that you’ll supply as an anonymous function using trailing closure syntax. Before the anonymous function is called, a special temporary autorelease pool is created, and is used for all autoreleased objects thereafter. After the anonymous function exits, the temporary autorelease pool is drained and goes out of existence. Here’s the same method with an autoreleasepool
call wrapping the inner loop:
func test() { let path = Bundle.main.path(forResource:"001", ofType: "png")! for j in 0 ..< 50 { autoreleasepool { for i in 0 ..< 100 { let im = UIImage(contentsOfFile: path) } } } }
The difference in memory usage is dramatic: memory holds roughly steady at less than 2MB (Figure 12-2). Setting up and draining the temporary autorelease pool probably involves some overhead, so if possible you may want to divide your loop into an outer and an inner loop, as shown in the example, so that the autorelease pool is not set up and torn down on every iteration.
Before ARC, managing memory for instance properties (Objective-C instance variables, Chapter 10) was one of the trickiest parts of Cocoa programming. The correct behavior is to retain a reference type object when you assign it to a property, and then release it when either of these things happens:
You assign a different value to the same property.
The instance whose instance property this is goes out of existence.
Memory management for a property therefore had to be distributed in two places:
The setter must release whatever object is currently the value of the property, and must retain whatever object is being assigned to that property. The exact details can be quite tricky (what if they are the same object?), and before ARC it was easy for programmers to get them wrong.
dealloc
methodThis is the Objective-C equivalent of deinit
. This method must be implemented to release every object being retained as the value of a property, or the object will leak when the owner goes out of existence.
Fortunately, ARC understands all that, and in Swift the memory of instance properties, like the memory of all variables, is managed correctly for you.
That fact also gives us a clue as to how to release an object on demand when you are holding it in an instance property. This is a valuable thing to be able to do, because an object may be using a lot of memory. You don’t want to put too great a strain on the device’s memory, so you want to release the object as soon as you’re done with it. Also, when your app goes into the background and is suspended, the Watchdog process will terminate it in the background if it is found to be using too much memory; so you might want to release this object when you are notified that the app is about to be backgrounded.
You can’t call release
explicitly, so you need another way to do it, some way that is consonant with the design and behavior of ARC. The solution is to assign something else — something small — to this property. That causes the object that was previously the value of this property to be released. A commonly used approach is to type this property as an Optional. This means that nil
can be assigned to it, purely as a way of replacing the object that is the instance property’s current value and releasing it.
As I explained in Chapter 5, you can get yourself into a retain cycle where two reference type objects have references to one another: for example, each is the value of the other’s instance property. If such a situation is allowed to persist until no other objects have a reference to either of these objects, then neither can go out of existence, because each has a retain count greater than zero and neither will “go first” and release the other. Since these two objects, ex hypothesi, can no longer be referred to by any object except one another, this situation can now never be remedied — these objects are leaking.
The solution is to step in and modify how the memory is managed for one of these references. By default, a reference is a strong reference: assigning to it retains the assigned value. In Swift, you can declare a reference type variable as weak
or as unowned
to change the way its memory is managed:
weak
When a reference is weak
, ARC does not retain the object assigned to it. This seems dangerous, because it means that the object might go out of existence behind our backs, leaving us with a dangling pointer and leading to a potential crash later on. But ARC is very clever about this. A reference marked as weak
must be a var
reference to an Optional. ARC keeps track of all weak references and all objects assigned to them. When such an object’s retain count drops to zero and the object is about to be destroyed, just before the object’s deinit
is called, ARC sneaks in and assigns nil
to the reference. Provided you handle the Optional coherently (by coping with the fact that it might suddenly be nil
), nothing bad can happen.
unowned
An unowned
reference is a different kettle of fish. When you mark a reference as unowned
, you’re telling ARC to take its hands off completely: it does no memory management at all when something is assigned to this reference. This really is dangerous — if the object referred to goes out of existence, you really can be left with a dangling pointer and you really can crash. That is why you must never use unowned
unless you know that the object referred to will not go out of existence: unowned
is safe, provided the object referred to will outlive the object that refers to it. So an unowned
reference should point at all times to some single independent object, retained in some other way,
without which the referrer cannot exist at all.
A weak reference is commonly used to connect an object to its delegate (Chapter 11):
class ColorPickerController : UIViewController { weak var delegate: ColorPickerDelegate? // ... }
A delegate is an independent entity; there is usually no reason why an object needs to claim ownership of its delegate. Indeed, an object is usually its delegate’s servant, not its owner, and ownership, if there is any, runs the other way; Object A might create and retain Object B, and make itself Object B’s delegate:
let cpc = ColorPickerController(colorName:colorName, color:c) cpc.delegate = self self.present(cpc, animated: true) // retains cpc
There’s no danger of a retain cycle in that code, because the delegate
property is weak
. This view controller (self
) is not somehow retaining itself.
Very rarely, you may encounter properties of built-in Cocoa classes that keep weak references as non-ARC weak references (because they are old and backward compatible, whereas ARC is new). Such properties are declared in Objective-C using the keyword assign
. NSCache’s delegate
property is declared like this:
@property (nullable, assign) id<NSCacheDelegate> delegate;
In Swift, that declaration is translated like this:
unowned(unsafe) var delegate: NSCacheDelegate?
The Swift term unowned
and the Objective-C term assign
are synonyms; they tell you that there’s no ARC memory management here. The unsafe
designation is a further warning inserted by Swift; unlike your own code, where you won’t use unowned
unless it is safe, Cocoa’s unowned
is potentially dangerous and you need to exercise caution.
A reference such as an NSCache’s delegate
can end up as a dangling pointer, pointing at garbage, if the object to which that reference was pointing has gone out of existence. If anyone tries to send a message by way of such a reference, the app will then crash. This is the delegate, so what usually happens is that Cocoa tries to send it a delegate message. The tell-tale sign of such a crash is that EXC_BAD_ACCESS
is reported somewhere in objc_msgSend
(Figure 12-3).
Figuring out the cause of a crash like that can be quite difficult, especially since the crash itself typically takes place long after the point where the real mistake occurred, namely that some object went out of existence while a reference to it continued to exist. You might not even know what object went out of existence. (This is the sort of situation in which you might need to “turn on zombies” in order to debug, as I’ll describe at the end of this chapter.)
Non-ARC weak references of this kind are few and far between in Cocoa nowadays; but they were once relatively common, so the earlier the iOS version on which you’re running, the more likely you are to encounter one. Fortunately, it’s easy to avoid a dangling pointer by making sure there is always something to point to. With an NSCache’s delegate
, if the delegate object is about to go out of existence at a time when the NSCache instance still exists, we would assign nil
(or some other object) to the delegate
property, rendering it harmless.
This section discusses some situations that call for some special memory management handling on your part.
Recall the example I gave in Chapter 11, where you register with the notification center by calling addObserver(forName:object:queue:using:)
, like this:
let ob = NotificationCenter.default.addObserver( forName: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil, queue: nil) { _ in self.updateNowPlayingItem() // ... and so on ... } self.observers.insert(ob as! NSObject)
I didn’t tell you at the time, but that code has the potential to cause a serious memory leak.
The reason is that the observer token object (ob
) returned from the registration call is retained by the notification center until you unregister it. (I regard this as something of a dirty trick on the notification center’s part; I think I understand Apple’s reasoning in designing things this way, but I also think they reasoned incorrectly.)
So the observer token object is likely to leak — but that isn’t the serious part. The serious part is that the observer token object is also retaining the view controller (self
) through the anonymous function. The reason is that functions are closures, and this function refers to self
. So the view controller is leaking along with the observer token. That’s bad. A view controller together with its properties, including its view, constitutes a heavyweight object and needs to go out existence when it is no longer needed. You cannot solve the problem merely by unregistering the observer in the view controller’s deinit
, because ex hypothesi deinit
isn’t going to be called; that is what leaking means.
The solution to the leakage of self
is to mark self
as weak
or (preferably) unowned
in the anonymous function. Now deinit
will be called and the view controller can go out of existence in good order. But we must still remember to unregister the observer token object, because otherwise the observer token itself will still leak, and its function can be called again even though self
no longer exists (and if we marked self
as unowned
, the app will crash at that moment with a dangling pointer). A complete solution looks something like this:
var observers = Set<NSObject>() override func viewDidLoad() { super.viewDidLoad() let ob = NotificationCenter.default.addObserver( forName: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil, queue: nil) { [unowned self] _ in self.updateNowPlayingItem() // ... and so on ... } self.observers.insert(ob as! NSObject) } deinit { for ob in self.observers { NotificationCenter.default.removeObserver(ob) } }
The NSKeyValueObservation observer object that you get when you call observe(_:options:changeHandler:)
(Chapter 11) is quite similar to the observer token object you get from the notification center. You maintain a reference to the observer object, because otherwise the notification message won’t arrive. And you’ll probably want to let the observer object just go out of existence when you yourself go out of existence, because it will then unregister itself automatically, which is exactly what you want.
The problem is that if your notification function refers to self
, that’s a retain cycle, because you are retaining an observer whose function is also retaining you. Therefore you won’t go out of existence, so the observer won’t go out of existence either and won’t be automatically unregistered. The solution, once again, is to mark self
as unowned
in the notification function:
var obs = Set<NSKeyValueObservation>() func registerWith(_ mc:MyClass1) { let opts : NSKeyValueObservingOptions = [.old, .new] let ob = mc.observe(\.value, options: opts) { [unowned self] obj, change in print(self) // potential leak } obs.insert(ob) }
The class documentation for Timer (Chapter 10) says that “run loops maintain strong references to their timers”; it then says of scheduledTimer(timeInterval:target:selector:userInfo:repeats:)
that “The timer maintains a strong reference to target
until it (the timer) is invalidated.” This should set off alarm bells in your head: “Danger, Will Robinson, danger!” The documentation is warning you that as long as a repeating timer has not been invalidated, the target is being retained by the run loop; the only way to stop this is to send the invalidate
message to the timer. (With a non-repeating timer, the problem arises less starkly, because the timer invalidates itself immediately after firing.)
Moreover, the target:
argument is probably self
. This means that you (self
) are being retained, and cannot go out of existence until you invalidate the timer. So when will you that? You can’t do it in your deinit
implementation, because as long as the timer is repeating and has not been sent the invalidate
message, deinit
won’t be called. You therefore need to find another appropriate moment for sending invalidate
to the timer, such as viewDidDisappear
:
var timer : Timer! override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(fired), userInfo: nil, repeats: true) self.timer.tolerance = 0.1 } @objc func fired(_ t:Timer) { print("timer fired") } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) self.timer.invalidate() }
Instead, a more flexible approach is to call the scheduledTimer(withTimeInterval:repeats:block:)
class method. Now there is no retained target:
— but there is a retained function. If the timer is a repeating timer, you are retaining it so that you can invalidate it later, but the timer is retaining the function, and if that function involves a reference to self
, it will retain self
, causing a retain cycle. But we know what to do about that! Mark self
as weak
or unowned
in the function. Now you can invalidate the timer in deinit
. This is similar to the two-part solution I described earlier for notification observer token objects:
var timer : Timer! override func viewDidLoad() { super.viewDidLoad() self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [unowned self] t in // * self.fired(t) } } func fired(_ t:Timer) { print("timer fired") } deinit { self.timer.invalidate() // * }
Other Cocoa objects with unusual memory management behavior will usually be called out clearly in the documentation.
A CAAnimation object retains its delegate; this is exceptional and can cause serious trouble if you’re not conscious of it (as usual, I speak from bitter experience).
There are also situations where the documentation fails to warn of any special memory management considerations, but you can wind up with a retain cycle anyway. Discovering the problem can be tricky. Areas of Cocoa that have given me trouble include UIKit Dynamics (a UIDynamicBehavior’s action
handler) and WebKit (a WKWebKit’s WKScriptMessageHandler).
Three Foundation collection classes — NSPointerArray, NSHashTable, and NSMapTable — are similar respectively to NSMutableArray, NSMutableSet, and NSMutableDictionary, except that (among other things) their memory management policy is up to you. An NSHashTable created with the weakObjects
class method maintains ARC-weak references to its elements, meaning that they are replaced by nil
if the retain count of the object to which they were pointing has dropped to zero. You may find uses for these classes as a way of avoiding retain cycles.
When a nib loads, it instantiates its nib objects (Chapter 7). What happens to these instantiated objects? A view retains its subviews, but what about the top-level objects, which are not subviews of any view? They do not have elevated retain counts; if someone doesn’t immediately retain them, they’ll simply vanish in a puff of smoke.
If you don’t want that to happen — and if you did, why would you be loading this nib in the first place? — you need to capture a reference to the top-level objects instantiated from the nib. There are two ways of doing that.
The first approach is to capture the result of the nib-loading code. When a nib is loaded by calling Bundle’s loadNibNamed(_:owner:options:)
or UINib’s instantiate(withOwner:options:)
, an array is returned consisting of the top-level objects instantiated by the nib-loading mechanism. So it’s sufficient to retain this array, or the objects in it. We did that in Chapter 7 when we loaded a nib and assigned the result to a variable, like this:
let arr = Bundle.main.loadNibNamed("View", owner: nil)! let v = arr[0] as! UIView self.view.addSubview(v)
The other possibility is to configure the nib owner with outlets that will retain the nib’s top-level objects when they are instantiated. We did that in Chapter 7 when we set up an outlet like this:
class ViewController: UIViewController { @IBOutlet var coolview : UIView!
We then loaded the nib with this view controller as owner:
Bundle.main.loadNibNamed("View", owner: self) self.view.addSubview(self.coolview)
In the first line, the nib-loading mechanism instantiates the top-level view from the nib and assigns it to self.coolview
. Since self.coolview
is a strong reference, it retains the view, and the view is still there when we insert it into the interface in the second line.
In real life, @IBOutlet
properties that will be set by the loading of a nib are usually marked weak
. Such outlet properties work properly as long as the object referred to will be retained by someone else — for instance, because it’s already a subview of your view controller’s main view. A view controller retains its main view, and a view is retained by its superview, so the nib-loading process will cause this view to be retained, and there is no need for your @IBOutlet
property to retain it as well.
A CFTypeRef is a pure C analog to an Objective-C object. In Objective-C, CFTypeRef types are distinguished by the suffix Ref
at the end of their name; in Swift, this Ref
suffix is dropped. For instance, a CGContextRef is a CFTypeRef, and is known in Swift as a CGContext.
A CFTypeRef is a pointer to an opaque C struct (see Appendix A), where “opaque” means that the struct has no directly accessible components. This struct acts as a pseudo-object; a CFTypeRef is analogous to an object type. In Objective-C, the fact that this thing is not an object is particularly obvious, because the code that operates upon a CFTypeRef is not object-oriented. A CFTypeRef has no properties or methods, and you do not send any messages to it; you work with CFTypeRefs entirely through global C functions. In Swift’s Core Graphics overlay, however, those global C functions are hand-tweaked to look like methods; for instance, the CGContextDrawLinearGradient
C function is called, in Swift, by sending drawLinearGradient(_:start:end:options:)
to a CGContext pseudo-object, just as if a CGContext were an object and drawLinearGradient
were an instance method.
Here’s some actual Swift code for drawing a gradient; con
is a CGContext, sp
is a CGColorSpace, and grad
is a CGGradient (all of them being CFTypeRefs):
let con = UIGraphicsGetCurrentContext()! let locs : [CGFloat] = [ 0.0, 0.5, 1.0 ] let colors : [CGFloat] = [ 0.8, 0.4, // starting color, transparent light gray 0.1, 0.5, // intermediate color, darker less transparent gray 0.8, 0.4, // ending color, transparent light gray ] let sp = CGColorSpaceCreateDeviceGray() let grad = CGGradient(colorSpace: sp, colorComponents: colors, locations: locs, count: 3)! con.drawLinearGradient(grad, start: CGPoint(x:89,y:0), end: CGPoint(x:111,y:0), options:[])
Despite being only a pseudo-object, a CFTypeRef is a reference type, and its memory must be managed in just the same way as that of a real object. Therefore, a CFTypeRef pseudo-object has a retain count! And this retain count works exactly as for a true object, in accordance with the golden rule of memory management. A CFTypeRef must be retained when it comes within the sphere of influence of an owner who wants it to persist, and it must be released when that owner no longer needs it.
In Objective-C, the golden rule, as applied to CFTypeRefs, is that if you obtained a CFTypeRef object through a function whose name contains the word Create
or Copy
, its retain count has been incremented. In addition, if you are worried about the object persisting, you’ll retain it explicitly by calling the CFRetain
function to increment its retain count. To balance your Create
, Copy
, or CFRetain
call, you must eventually release the object. By default, you’ll do that by calling the CFRelease
function; some CFTypeRefs, however, have their own dedicated object release functions — for CGPath, for instance, there’s a dedicated CGPathRelease
function. There’s no ARC management of CFTypeRefs in Objective-C, so you have to do all of this yourself, explicitly.
In Swift, however, you will never need to call CFRetain
, or any form of CFRelease
; indeed, you cannot. Swift will do it for you, behind the scenes, automatically.
Think of CFTypeRefs as living in two worlds: the CFTypeRef world of pure C, and the memory-managed object-oriented world of Swift. When you obtain a CFTypeRef pseudo-object, it crosses the bridge from the CFTypeRef world into the Swift world. From that moment on, until you are done with it, it needs memory management. Swift is aware of this, and for the most part, Swift itself will use the golden rule and will apply correct memory management. The code I showed earlier for drawing a gradient is in fact memory-management complete. In Objective-C, we would have to release sp
and grad
, because they arrived into our world through Create
calls; if we failed to do this, they would leak. In Swift, however, there is no need, because Swift will do it for us. (See Appendix A for more about how objects move between the CFTypeRef world and the memory-managed object world.)
Working with CFTypeRefs in Swift is much easier than in Objective-C. In Swift, you can treat CFTypeRef pseudo-objects as actual objects! You can assign a CFTypeRef to a property in Swift, or pass it as an argument to a Swift function, and its memory will be managed correctly; in Objective-C, those are tricky things to do.
It is possible that you may receive a CFTypeRef through some API that lacks memory management information. Such a value will come forcibly to your attention, because it will arrive into Swift, not as a CFTypeRef, but as an Unmanaged generic wrapping the actual CFTypeRef. That situation alerts you to the fact that Swift does not know how to proceed with the memory management of this pseudo-object. You will be unable to proceed until you unwrap the CFTypeRef by calling the Unmanaged object’s takeRetainedValue
or takeUnretainedValue
method. You will call whichever method tells Swift how to manage the memory for this object correctly. For a CFTypeRef with an incremented retain count (usually acquired through a function with Create
or Copy
in its name), call takeRetainedValue
; otherwise, call takeUnretainedValue
.
In Objective-C, a @property
declaration (see Chapter 10) includes a statement of the memory management policy implemented by the corresponding setter accessor method. It is useful to be aware of this and to know how such policy statements are translated into Swift.
Earlier I said that a UIViewController retains its view
(its main view). How do I know this? Because the @property
declaration tells me so:
@property(null_resettable, nonatomic, strong) UIView *view;
The term strong
means that the setter retains the incoming UIView object. The Swift translation of this declaration doesn’t add any attribute to the variable:
var view: UIView!
The default in Swift is that a variable referring to a reference object type is a strong reference. This means that it retains the object. You can safely conclude from this declaration that a UIViewController retains its view
.
The possible memory management policies for a Cocoa property are:
strong
, retain
(no Swift equivalent term)The default. The two terms are pure synonyms; retain
is the term inherited from pre-ARC days. Assignment to this property releases the existing value (if any) and retains the incoming value.
copy
(Swift @NSCopying
)The same as strong
or retain
, except that the setter copies the incoming value by sending copy
to it; the incoming value must be an object of a type that adopts NSCopying, to ensure that this is possible. The copy, which has an increased retain count already, becomes the new value.
weak
(Swift weak
)An ARC-weak reference. The incoming object value is not retained, but if it goes out of existence behind our back, ARC will magically substitute nil
as the value of this property, which must be typed as an Optional declared with var
.
assign
(Swift unowned(unsafe)
)No memory management. This policy is inherited from pre-ARC days, and is inherently unsafe (hence the additional unsafe
warning in the Swift translation of the name): if the object referred to goes out of existence, this reference will become a dangling pointer and can cause a crash if you subsequently try to use it.
The copy
policy is used by Cocoa particularly when an immutable class has a mutable subclass (such as NSString and NSMutableString, or NSArray and NSMutableArray; see Chapter 10). The idea is to deal with the danger of the setter’s caller passing an object of the mutable subclass. This is possible because, in accordance with the substitution principle of polymorphism (Chapter 4), wherever an instance of a class is expected, an instance of its subclass can be used instead. It would be bad if this were to happen, because now the caller might keep a reference to the incoming value and, since it is in fact mutable, could later mutate it behind our back. To prevent this, the setter calls copy
on the incoming object; this creates a new instance, separate from the object provided — and belonging to the immutable class.
In Swift, this problem is unlikely to arise with strings and arrays, because on the Swift side these are value types (structs) and are effectively copied when assigned, passed as an argument, or received as a return value. Cocoa’s NSString and NSArray property declarations, when translated into Swift as String and Array property declarations, don’t show any special marking corresponding to Objective-C copy
. But Cocoa types that are not bridged to Swift value types do show a marking: @NSCopying
. The declaration of the attributedText
property of a UILabel appears like this in Swift:
@NSCopying var attributedText: NSAttributedString?
NSAttributedString has a mutable subclass, NSMutableAttributedString. You’ve probably configured this attributed string as an NSMutableAttributedString, and now you’re assigning it as a UILabel’s attributedText
. The UILabel doesn’t want you keeping a reference to this mutable string and mutating it in place, since that would change the value of the property without passing through the setter. It copies the incoming value to ensure that what it has is a separate immutable NSAttributedString.
You’ll want to do the same thing in your own code, and you can. Simply mark your property with the @NSCopying
attribute; Swift will enforce the copy
policy and will take care of the actual copying for you whenever this property is assigned to:
class StringDrawer { @NSCopying var attributedString : NSAttributedString! // ... }
If, as is sometimes the case, your own class wants the internal ability to mutate the value of this property while preventing a mutable value from arriving from outside, put a private computed property façade in front of it whose getter transforms it to the corresponding mutable type:
class StringDrawer { @NSCopying var attributedString : NSAttributedString! private var mutableAttributedString : NSMutableAttributedString! { get { if self.attributedString == nil {return nil} return NSMutableAttributedString( attributedString:self.attributedString) } set { self.attributedString = newValue } } // ... }
@NSCopying
can be used only for instance properties of classes, not of structs or enums — and only in the presence of Foundation, because that is where the NSCopying protocol is defined, which the type of a variable marked as @NSCopying
must adopt.
Though far less likely to occur under ARC (and Swift), memory management mistakes can still occur, especially because a programmer is apt to assume that they can’t. Experience suggests that you should use every tool at your disposal to ferret out possible mistakes. Here are some of those tools (and see Chapter 9):
The memory gauge in the Debug navigator charts memory usage whenever your app runs, allowing you to observe possible memory leakage or other unwarranted heavy memory use. Note that memory management in the Simulator is not necessarily indicative of reality! Always observe the memory gauge with the app running on a device before making a judgment.
Instruments (Product → Profile) has excellent tools for discerning leaks and tracking memory management of individual objects (Leaks, Allocations).
Good old caveman debugging can help confirm that your objects are behaving as you want them to. Implement deinit
with a print
call. If it isn’t called, your object is not going out of existence. This technique can reveal problems that even Instruments will not directly expose.
Memory graphing (“Memory Debugging”) will draw you a picture of the ownership relations between your objects; in conjunction with Malloc Stack, it will trace that ownership through the actual retain calls.
Dangling pointers are particularly difficult to track down, but they can often be located by “turning on zombies.” This is easy in Instruments with the Zombies template (on a device). Alternatively, edit the Run action in your scheme, switch to the Diagnostics tab, and check Enable Zombie Objects. The result is that an object that goes out of existence is replaced by a “zombie” that will report to the console if a message is sent to it (“message sent to deallocated instance”). Moreover, the zombie knows what kind of object it replaces, so you can learn what got deallocated. Be sure to turn zombies back off when you’ve finished tracking down your dangling pointers. Don’t use zombies with the Leaks instrument: zombies are leaks.
The Address Sanitizer (also in the scheme’s Run action’s Diagnostics tab) lets you debug even more subtle forms of memory misuse. Here we’re doing a Very Bad Thing, writing directly into memory that doesn’t belong to us:
let b = UnsafeMutablePointer<CGFloat>.allocate(capacity:3) b.initializeFrom([0.1, 0.2, 0.3]) b[4] = 0.4
That code probably won’t crash; it corrupts memory silently, which is even worse. But if we run our app under Address Sanitizer, it detects the problem and reports a heap buffer overflow.