Beruflich Dokumente
Kultur Dokumente
Russ Bishop
PlanGrid
http://russbishop.net
The Swift Language
• Productivity
• isa oops
• Undefined Behavior
• When there are three functions in the whole project to audit, everyone says OK.
• When there are 300 files to audit, everyone throws up their hands and says “we
don’t have time”.
• Calling “unsafe” code from Swift makes operations more explicit, easier to reason
about
If you’re lucky:
If you’re unlucky…
- (void) doBadThing
{
int *a = malloc(1); 1FC0 05
*a = 5;
[self printValue:a]; 00
} 00
- (void) printValue:(int *)a 1FC4 00
{
NSLog(@"Value is %d", *a);
}
No crash… yet.
Just corrupted memory.
The Toolbox
Let’s run with safety scissors first
Less Dangerous
Strings
Wait, what?
• You can include strong object references in them, something ARC disallows for C
structs
• It also needs that storage to be alive. A pointer is dumb, says nothing of lifetime
• withCString promises that the pointer will be contiguous and alive for the duration of
the closure.
• Also why it’s @noescape - because if the closure outlived the call then it would have
a dangling pointer.
Lifetimes & Ownership
• Arrays look like structs and have value semantics
• But really the structs are just headers, the item storage is on the
heap
• Does adding two arrays copy them or just use a linked list of slices?
Doesn’t matter, it’s invisible to us!
• passRetained / passUnretained
• takeRetainedValue / takeUnretainedValue
let o = OhMy()
Less Dangerous
Unmanaged
let o = OhMy()
//Unbalanced retain +1
let context = Unmanaged.passRetained(o).toOpaque()
Less Dangerous
Unmanaged
let o = OhMy()
//Unbalanced retain +1
let context = Unmanaged.passRetained(o).toOpaque()
let o = OhMy()
//Unbalanced retain +1
let context = Unmanaged.passRetained(o).toOpaque()
dispatch_queue_set_specific(queue,
&Keys.SomeKey,
contextPointer,
nil)
Less Dangerous
Unmanaged
• Unfortunately in that last example, we’d really like to pass a function as
the last parameter
• You can receive them and pass them along, but you can’t provide them or
call them**
• But it does prove it would be possible for Swift to allow this in the future
Unsafe Pointers
Less Dangerous
UnsafePointer / UnsafeMutablePointer
let nullPtr = UnsafeMutablePointer<Int>(nil)
Null Pointer
Less Dangerous
UnsafePointer / UnsafeMutablePointer
let nullPtr = UnsafeMutablePointer<Int>(nil)
println(nullPtr.memory)
EXC_BAD_ACCESS
Less Dangerous
UnsafePointer / UnsafeMutablePointer
let nullPtr = UnsafeMutablePointer<Int>(nil)
println(nullPtr.memory)
Equivalent to malloc(sizeof(Int))
Less Dangerous
UnsafePointer / UnsafeMutablePointer
let nullPtr = UnsafeMutablePointer<Int>(nil)
println(nullPtr.memory)
let nullPtr = UnsafeMutablePointer<Int>(nil)
println(nullPtr.memory)
ptr.initialize(5)
ptr.destroy()
ptr’s destination uninitialized,
but memory is still allocated
Less Dangerous
UnsafePointer / UnsafeMutablePointer
let nullPtr = UnsafeMutablePointer<Int>(nil)
println(nullPtr.memory)
ptr.initialize(5)
ptr.destroy()
• You may notice that with UnsafePointers, Swift treats them similarly
to C
• moveAssignFrom does the same but destroys any objects that already exist in the
current pointer first
• Or just subscript
var values = UnsafeMutablePointer<some_struct_t>()
for i in 0..<getSomeStructs(&values) {
let value: some_struct_t = values[i]
}
Less Dangerous
AutoreleasingUnsafeMutablePointer (✨Magic✨)
• Allows implicit conversions to cover in/out parameters. You’re already using it - anything that takes
NSError**!
• On return, the reference is copied back into myObj which will retain it.
• If it were a normal UnsafeMutablePointer<T>, Swift would assume the caller was returning a
+1 retained object and not bother retaining it again.
UnsafePointer Example
let host = "www.apple.com"
let port = "80"
UnsafePointer Example
let host = "www.apple.com"
let port = "80"
• Now you too can implement your own version of Array! (or any other
fancy data structure your heart desires)
init(capacity:Int) {
_buffer = CustomArrayBuffer<Int,Element>.create(capacity,
initialValue: { (proto) -> Int in
return capacity
}) as! CustomArrayBuffer<Int,Element>
}
init(capacity:Int) {
_buffer = CustomArrayBuffer<Int,Element>.create(capacity,
initialValue: { (proto) -> Int in
return capacity
}) as! CustomArrayBuffer<Int,Element>
}
• Just a bag-o-bits
• Otherwise safe because it isn’t legal to use the va_list after returning
Variadic Arguments
typedef struct some_struct {
NSInteger a;
NSInteger b;
NSInteger c;
} some_struct_t;
//Swift Code:
extension some_struct_t : CVarArgType {
public func encode() -> [Word] {
return self.a.encode() + self.b.encode() + self.c.encode()
}
}
• Profile first! Unless this is a performance problem, it’s not worth it.
unsafeDowncast
• In debug builds, works exactly like ‘as’
• If you’re wrong:
• I highly recommend you understand the ABI and how Swift will
marshal the parameters; you won’t get much compiler help here.
• For APIs that want a key (or void *), just declare a static var
initialized to a unique constant string, then pass &myStaticVar
Resources
• NSHipster - http://nshipster.com
• objc.io - http://www.objc.io
• my blog: http://russbishop.net
Fin.