Swift v1.2 is out, and, well, it’s pretty fab actually.
The most exciting part for me is the beefed-up if let syntax. Not only does it allow you to unwrap multiple optionals at once, you can define a later let in terms of an earlier one. For instance, here are 3 functions, each one of which takes a non-optional and returns an optional, chained together:
if let url = NSURL(string: urlString),
let data = NSData(contentsOfURL: url),
let image = UIImage(data: data) {
// display image
}
The key here is that if at any of the preceding lets fail, the whole thing fails and the later functions are not called. If you’re already familiar with optional chaining with ?. this should come naturally to you. For more on this, see NSHipster’s article.
The new ability to defer initialization of let constants makes much of of my “avoiding var” post redundant, as you can now do this: 1
let direction: Direction
switch (char) {
case "a": direction = .Left
case "s": direction = .Right
default: direction = .Forward
}
It’d still be nice to have a form of switch that evaluates to a value – but with the new let, that’s really just a desire for some syntactic sugar (imagine using it inside a short closure expression, without needing any return).
Speaking of switch, the new if, with the ability to have a where clause on the bound variables, means if may now be suitable for several places where you were previously using switch. But there’s still a place for switch – especially when using it for full coverage of each case of an enum. And I feel a bit like pattern matching via ~= is one of switch’s powerful overlooked features that I keep meaning to write a post about. This release adds a ~= operator for ranges, not just intervals.
Library Additions
The big new addition to the standard library is of course the Set collection type. The focus on mathematical set operations is very nice, and also that those operations take any SequenceType, not just another Set:
let alphabet = Set("abcdefghijklmnopqrstuvwxyz")
let consonants = alphabet.subtract("aeiou")
// don't forget, it's "jumps" not "jumped"
let foxy = "the quick brown fox jumped over the lazy dog"
alphabet.isSubsetOf(foxy) // false
Note that the naming conventions emphasize the pure versions of these operations, which create new sets, with each operation having a mutating equivalent with an InPlace suffix:
// create a new set
var newalpha = consonants.union("aeiou")
// modify an existing set
newalpha.subtractInPlace(consonants)
Note also for those fearful of the evil of custom operators, there’s no - for set subtraction etc. The one operator defined for sets is ==. Unlike with Array, sets are also Equatable (since they mandate their contents are Equatable, via Hashable).
But that’s not all. Here’s a rundown of some of the other library changes:
- There‘s no longer a separate
countElementscall for counting the size of collections vscountfor ranges – it’s now justcountfor both. Dictionary‘s index, which used to be bi-directional, is now forward-only, as are the lazy collections you get back fromkeysandvalues.- The
C_ARGCandC_ARGVvariables are gone, as is the_Processprotocol. In their place there’s now aProcessenum with 3 static methods:arguments, which gives you a[String]of the arguments, andargcandargvwith give you the raw C-string versions. - Protocols that used to declare
class funcs now declarestatic funcs since the language now requires that. Array,ClosedIntervalandSlicenow conform to a new protocol,_DestructorSafeContainer. This protocol indicates “a container is destructor safe if whether it may store to memory on destruction only depends on its type parameters.” i.e. an array ofIntis known never to allocate memory when its destroyed sinceIntdoesn’t, whereas a more complex type (with adeinit, or aggregating a type with one) may not be.- They also now conform to a new
__ArrayTypeprotocol. Two underscores presumably means you definitely definitely shouldn’t try and conform to this directly yourself. - The builtin-converting
Array.convertFromHeapArrayfunction is gone. - The pointer types (
AutoreleasingUnsafeMutablePointer,UnsafePointerandUnsafeMutablePointernow conform to a new_PointerTypeprotocol. - The
CVaListPointer(the equivalent of C’sva_list) now has aninitfrom an unsafe pointer. EmptyGeneratornow has a public initializer, so you can use it directly.- The various unicode types now have links in their docs to the relevant parts of unicode.org’s glossary
HeapBufferandHeapBufferStorageare gone, replaced by the more comprehensive and documentedManagedBufferandManagedBufferPointer, of which more later.ObjectIdentifieris nowComparable.- The enigmatic
OnHeapstruct is gone. - All the views (
UnicodeScalarView,UTF8ViewandUTF8View) insideStringare nowPrintableandDebugPrintable String.lowercaseStringanduppercaseStringare back! I missed you guys.- The various
convertFromstatic functions inStringInterpolationConvertibleare nowinits. UnsafePointerandUnsafeMutablePointerno longer have a staticnull()instance, presumably you should just use the nil literal.- There’s a new
zipfunction, which returns aZip2struct – consistent with thelazyfunction that returnsLazyThings, and possibly a gateway toZipMoreThan2structs in the future…
The Printable and DebugPrintable protocol docs now point out: if you want a string representation of something, use the toString protocol – in case you were trying to get fancy with if let x as? Printable { use(x.description) }. Which the language will now let you do, since as? and is now work with Swift protocols, which is cool. But still don’t do that – especially considering you may be sorry when you find out String doesn’t conform to Printable.
The Character type is now a struct, not an enum, and no longer exposes a “small representation” and “large representation”. This appears to go along with a performance-improving refactoring that has made a big difference – for example, a simple bit of code that sucks in /usr/share/dict/words into a Swift String and counts the word lengths now runs in a quarter of the time it used to.
Bovine Husbandry
Suppose you wanted to write your very own collection, similar to Array, but managing all the memory yourself. Up until Swift 1.2, this was tricky to do.
The memory allocation part isn’t too hard – UnsafeMutablePointer makes it pretty straightforward (and typesafe) to allocate memory, create and destroy objects in that memory, move those objects to fresh memory when you need to expand the storage etc.
But what about de-allocating the memory? Structs have no deinit, and that unsafely-pointed memory ain’t going to deallocate itself. But you still want your collection to be a struct, just like Array is, so instead, you implement a class to hold the buffer, and make that a member. That class can implement deinit to destroy its contained objects and deallocate the buffer when the struct is destroyed.
But you’re still not done. At this point, you’re in a similar position Swift arrays were in way back in the first ever Swift beta. They were structs, but they didn’t have full value semantics. If someone copies your collection, they’ll get a bitwise copy of the struct, but all this will do is copy the reference to the buffer. The two structs will both point to the same buffer, and updating the values in one would update both. Behaviour when the buffer is expanded would depend on your implementation (if the buffer expanded itself, they might both see the change, if the struct replaced its own buffer, they might not).
How to handle this? One way would be to be able to detect when a struct is copied, and make a copy of its buffer. But just as structs don’t have deinit, they also don’t have copy constructors – this isn’t C++!
So plan B – let the bitwise copy happen, but when the collection is mutated (say, via subscript set), detect whether the buffer is referenced more than once, and at that point, if it is, make a copy of the buffer before changing it. This solves the problem, and also comes with a big side-benefit – your collection is now copy-on-write (COW). Arrays in Swift are also COW, and this means that if you make a copy of your array, but then don’t make any changes, no actual copying of the buffer takes place – the buffer is only copied when it has to be, because you are mutating the values of some shared storage.
Since classes in Swift are reference-counted, it ought to be possible to detect if your buffer class is being referenced by more than one struct. But there wasn’t an easy Swift-native way to do this, until 1.2, which introduces isUniquelyReferenced and isUniquelyReferencedNonObjC function calls:
extension MyFancyCollection: MutableCollectionType {
// snip...
subscript(idx: Index) -> T {
get {
return _buffer[idx]
}
set(newValue) {
if isUniquelyReferencedNonObjC(&_buffer) {
// all fine, this buffer is solely owned
// by this struct:
_buffer[idx] = newValue
}
else {
// uh-oh, someone else is referencing this buffer
_buffer = _buffer.cloneAndModify(idx, newValue)
}
}
}
Great, COW collections all round. But before you run off and start implementing your own buffer class for use with your collection, you probably want to check out the ManagedBuffer class implementation, and a ManagedBufferPointer struct that can be used to manage it. These provide the ability to create and resize a buffer, check its size, check if its uniquely referenced, access the underlying memory via the usual withUnsafeMutablePointer pattern etc. The commenting docs are pretty thorough so check them out.
- Just in case you want to try out this new feature in a playground – this technique does not work with globals. See this thread on the dev forums. ↩