I don't believe that there are any guarantees made in terms of reference count (since the optimizer may remove retain/release pairs that are provably unnecessary), but both forms of doSomething hold a strong reference to viewController. The difference is whether viewController itself can be reassigned:
class C {
var x: Int
init(x: Int) { self.x = x }
}
func doSomething(_ c: C) {
c.x += 1
}
func doSomethingInout(_ c: inout C) {
c = C(x: c.x + 1)
}
with inout on a reference type, we are allowed to reassign the entire instance to something else. When used, we would observe this behavior:
let c1 = C(x: 0)
let c2 = c1
var c3 = C(x: 0)
var c4 = c3
doSomething(c1) // OK
c1 === c2 // true
doSomethingInout(&c2) // Error!
doSomething(c3) // OK
c3 === c4 // true
doSomethingInout(&c3) // OK
c3 === c4 // false
Unfortunately it's relatively difficult to say in isolation, because it can depend on surrounding context, the contents of the functions, and how the compiler can transform things in various ways as it optimizes.
If I had to guess in isolation, neither will immediately do a retain, because the first one will pass viewController at +0, and the second one the ARC optimizer will probably successfully optimize the copy-in-copy-out.
Passing it to CFGetRetainCount() may actually be causing ARC to insert a retain in that case. Also, playgrounds can't reliably be used for this sort of thing because they run extra code on every line.
The only accurate ways to do this are:
lldb breakpoints on the various swift_retain/swift_release functions
reading the generated assembly code
Working on stdlib performance I do a lot of reading assembly code >.<
Also remember to compile with optimizations enabled