road before. • In theory unsafe{} blocks are no different than using C • In reality, it vastly limits the scope of potentially memory-unsafe operations. • 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”. • Definitely safe code is very distinct from potentially unsafe code • Calling “unsafe” code from Swift makes operations more explicit, easier to reason about
structs and enums • You can include strong object references in them, something ARC disallows for C structs • A function expecting a C string needs the storage to be contiguous. Something not necessarily guaranteed by String (or Array!) • 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.
value semantics • But really the structs are just headers, the item storage is on the heap • Shared when it can be, copy-on-mutate • Does adding two arrays copy them or just use a linked list of slices? Doesn’t matter, it’s invisible to us! • So pass them with &myArray or use withUnsafeBufferPointer!
Swift Object) • fromOpaque / toOpaque allow conversion from/to COpaquePointer (aka void*) • passRetained / passUnretained • Get objects into Unmanaged<T> • takeRetainedValue / takeUnretainedValue • Get objects back out of Unmanaged<T> • retain / release / autorelease • Relive the glory days of Manual Reference Counting Unmanaged
like to pass a function as the last parameter • Swift currently has no support for exposing C function pointers • You can receive them and pass them along, but you can’t provide them or call them** • ** If you get an UnsafePointer to a function it’s a wrapper struct with a function object that itself contains a function address but that’s just asking for random crashes and breakage. • But it does prove it would be possible for Swift to allow this in the future Unmanaged
for you • When in doubt, try passing as &myVar • Arrays, Ints, structs all work • You can cast pointers by passing them to init() UnsafePointer / UnsafeMutablePointer
three states: • Unallocated - pointer is NULL or location was deallocated • Allocated - pointer points at a memory location, location is uninitialized • Initialized - pointer points to a memory location and that location has a valid value UnsafePointer / UnsafeMutablePointer
= UnsafeMutablePointer<Int>.alloc(1) ptr.initialize(5) ptr.destroy() UnsafePointer / UnsafeMutablePointer ptr’s destination uninitialized, but memory is still allocated
lifetime rules (if T supports it) • It’s like pointers had __strong attributes by default • It also means assigning to the memory property releases any existing object and retains the new one (or any nested references for structs/enums)
treats them similarly to C • Pretend they’re an array by subscripting • Only you know if the pointer is to a single element or an array of 10,000 elements. UnsafePointer / UnsafeMutablePointer
pointer. If the object isn’t retained elsewhere it will get cleaned up. • The source pointer becomes uninitialized in the process • moveInitializeFrom transfers ownership from some other pointer to this one, assuming this one is empty • If it isn’t then you’ll leak. • moveAssignFrom does the same but destroys any objects that already exist in the current pointer first • If it isn’t then you could crash. UnsafePointer / UnsafeMutablePointer
values = UnsafeMutablePointer<some_struct_t>() for i in 0..<getSomeStructs(&values) { let valuePtr = values + i ... } • Or just subscript var values = UnsafeMutablePointer<some_struct_t>() for i in 0..<getSomeStructs(&values) { let value: some_struct_t = values[i] } UnsafePointer / UnsafeMutablePointer
You’re already using it - anything that takes NSError**! • Nil turns into a NULL pointer • UnsafeMutablePointer<T> passed as-is • Object references (&myObj) are special • A temporary copy of the reference is allocated • 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. AutoreleasingUnsafeMutablePointer (✨Magic✨)
version of Array! (or any other fancy data structure your heart desires) • Define your own copy-on-write struct • Internally references a ManagedBuffer • On mutate, if isUniquelyReferenced then mutate in place • Otherwise make a copy and mutate that ManagedBuffer / ManagedBufferPointer
• Use withVaList; life of CVaListPointer is tied to the closure • Must implement CVarArgType • getVaList for cases where use-before-init rules prevent you from doing it properly • Auto-released object so has a runtime cost • Otherwise safe because it isn’t legal to use the va_list after returning
In release builds, doesn’t bother checking for nil • If you’re wrong, crash-city • Profile first! Unless this is a performance problem, it’s not worth it.
In release builds, doesn’t bother checking the type • It’s wrong isa all over again • If you’re wrong: • Objective-C unrecognized selector • Swift maybe unrecognized selector, maybe just memory corruption (depends on optimization) • Again, profile first!
type • Beta documentation comments described it as “brutal” • Release takes a softer approach: “There’s almost always a better way to do anything” • Personally, I agree. Nitroglycerin is extremely dangerous. • … but can be life-saving when you truly need it.
(even if deprecated) • … or you know, private Swift functions • I highly recommend you understand the ABI and how Swift will marshal the parameters; you won’t get much compiler help here. @asmname("dispatch_get_current_queue") func _get_current_queue() -> dispatch_queue_t