The Swift Programming Language (Swift 3.0.1) by Apple Inc (2016) (read in 2017)
Published by marco on
Disclaimer: these are notes I took while reading this book. They include citations I found interesting or enlightening or particularly well-written. In some cases, I’ve pointed out which of these applies to which citation; in others, I have not. Any benefit you gain from reading these notes is purely incidental to the purpose they serve of reminding me what I once read. Please see Wikipedia for a summary if I’ve failed to provide one sufficient for your purposes. If my notes serve to trigger an interest in this book, then I’m happy for you.
This is an incredibly detailed language specification for Swift and programming guide for iOS and OS X. The language itself is quite complex and includes so many programming features that it’s nearly dizzying. There are a lot of examples, as well as English-language translations of all of the examples. This leads to a lot of text, but you can get into the rhythm of skipping the few paragraphs after the examples to move on to the next concepts (especially if the code sample was abundantly clear).
The kitchen sink includes:
- Functional switch statements (matching)
- Inferred types
- Argument labels
- Very wordy Error-handling
- An unbelievable number of keywords:
autoclosure
: required when the developer wants to avoid closing over certain variables and creating cycles (which the reference-counter in Swift cannot break on its own)convenience
: identifies initialization methods that are not requiredconvention
:defer
: Swift’s version offinally
deprecated
: An attribute to indicate when a feature was deprecated (seeobsoleted
)discardableResult
: functions are pure and results must be used; add this attribute to allow a result to be ignoreddynamic
: trigger the compiler to use Objective-C–style method-dispatchescaping
: indicate that a lambda parameter is used outside of the called functionextension
: add features to a typeinfix
: operator typeintroduced
: An attribute to indicate when a feature was introducedlazy
: explicitly indicate that a property initialization should be delayed until it is first calledobsoleted
: An attribute to indicate when a feature was made obsolete (seedeprecated
)optional
: a feature of a protocol that does not have to be implemented in the implementing type (it can be satisfied by anextension
instead)postfix
: operator typeprefix
: operator typerequired
: identifies initialization methods that are not conveniencesubscript
: an indexer function (e.g.[x]
)testable
: an attribute that subverts the type/member/function visibility so that a test framework has access to internalsunowned
: use a weak reference that allows the owner to cascade-delete the object that has the unowned reference to itweak
: a classic weak reference: the reference will be set tonil
when the referenced object is deleted.- two types of generic constraints
- and so on…
Citations
“Sometimes it is clear from a program’s structure that an optional will always have a value, after that value is first set. In these cases, it is useful to remove the need to check and unwrap the optional’s value every time it is accessed, because it can be safely assumed to have a value all of the time.
“These kinds of optionals are defined as implicitly unwrapped optionals. You write an implicitly unwrapped optional by placing an exclamation mark (String!) rather than a question mark (String?) after the type that you want to make optional.”
“A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases. You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection.”
These are pretty interesting. The arithmetic example is a very elegant way of implementing this behavior.
“Use a weak reference when the other instance has a shorter lifetime—that is, when the other instance can be deallocated first. In the Apartment example above, it is appropriate for an apartment to be able to have no tenant at some point in its lifetime, and so a weak reference is an appropriate way to break the reference cycle in this case. In contrast, use an unowned reference when the other instance has the same lifetime or a longer lifetime.”
You can’t pass a struct or enum by reference, so you’re bound to value/ref usage at type-declaration time. That’s a pretty harsh restriction.
Deallocation is also immediate ([A]utomatic [R]eference [C]ounting takes care of it). This means that it is also predictable (unlike disposal/finalization in C#)
It also means, however, that allocation cycles can only be resolved with developer intervention
So….as usual, Swift solves it with multiple tools: weak
and unowned
references.
Weak = set to null
Unowned = cascade
“In systems that use garbage collection, weak pointers are sometimes used to implement a simple caching mechanism because objects with no strong references are deallocated only when memory pressure triggers garbage collection. However, with ARC, values are deallocated as soon as their last strong reference is removed, making weak references unsuitable for such a purpose.”
“You indicate an unsafe unowned reference by writing unowned(unsafe). If you try to access an unsafe unowned reference after the instance that it refers to is deallocated, your program will try to access the memory location where the instance used to be, which is an unsafe operation.”
“However, there is a third scenario, in which both properties should always have a value, and neither property should ever be nil once initialization is complete. In this scenario, it is useful to combine an unowned property on one class with an implicitly unwrapped optional property on the other class.”
“Because capitalCity has a default nil value, a new Country instance is considered fully initialized as soon as the Country instance sets its name property within its initializer. This means that the Country initializer can start to reference and pass around the implicit self property as soon as the name property is set. The Country initializer can therefore pass self as one of the parameters for the City initializer when the Country initializer is setting its own capitalCity property.”
This is a very curious way of handling initialization. The ordering of parameter initialization determines at which point self
can be used.
“Swift requires you to write self.someProperty or self.someMethod() (rather than just someProperty or someMethod()) whenever you refer to a member of self within a closure. This helps you remember that it’s possible to capture self by accident.”
“Sometimes you know a throwing function or method won’t, in fact, throw an error at runtime. On those occasions, you can write try! before the expression to disable error propagation and wrap the call in a runtime assertion that no error will be thrown. If an error actually is thrown, you’ll get a runtime error.”