dfri dfri - 2 months ago 8
Swift Question

Type erasure: do we risk non-reversibly loosing access to kept-alive data of the instance of the erased type, when erasing the type information?

Consider the following common simple type erasure scheme

protocol Foo {
associatedtype Bar
func bar() -> Bar
}

struct AnyFoo<Bar>: Foo {
private let _bar: () -> Bar

init<F: Foo>(_ foo: F) where F.Bar == Bar {
_bar = foo.bar
/* stores a reference to foo.bar,
so foo kept alive by ARC? */
}

func bar() -> Bar {
return _bar()
}
}


Assume the initializer argument
foo
above is (intended to be) a temporary instance of a "large" type, from which we're only interested to slice out the information blueprinted by
Foo
(i.e., the
bar()
method).

struct Huge { /* ... */ }

struct Foobar: Foo {
internal func bar() -> String {
return "foo"
}
let lotsOfData: Huge = Huge()
}

func getAnyFooStr() -> AnyFoo<String> {
let foobar = Foobar()
return AnyFoo(foobar)
}

let anyStrFoo = getAnyFooStr()
/* we can now access anyStrFoo.bar() (-> "foo") from our
erased type, but do lotsOfData of the underlying seemingly
temporary Foobar() instance still "live", unreachable? */



  • Q: Will we still keep the remaining content of
    foo
    alive and unreachable in memory due to the fact that closures are reference types? And if so, I assume we would never be able to reclaim access to this lost-but-alive content?



(I tried the above with
Foobar
as a class, monitoring the (lack of a)
deinit
call, but I've confused myself so much the last hour that I need some non-self verification for this, especially for the case when
Foobar
is a value type)





Xcode 8.0 / Swift 3.

Answer

Yes, and it has nothing to do with type erasure :) The closure Foobar().bar keeps the instance that it is bound to (with all its properties) alive as long as the closure is alive.

Here is a simplified example:

class Huge {
    deinit { print("deinit Huge") }
}

struct Foobar {
    internal func bar() -> String {
        return "foo"
    }
    let lotsOfData: Huge = Huge()
}

do {
    let fb  = Foobar().bar // the type of `fb` is `() -> String`
    print("still alive ...")
}
print("... out of scope now")

Output:

still alive ...
deinit Huge
... out of scope now
Comments