Variable initialization: Which is better in general, in-place or in-constructor?

Initialize variables in place:

final class View {
    var set: Set<SomeClass> = []
    var optional: SomeClass?

    let simpleInitializer: SomeClass = .init()
    let verboseInitializer: SomeClass = .init(
        arg1: 1,
        arg2: 2,
        arg3: 3,
        arg4: 4
    )

    let tupleArray: [(SomeClass, SomeClass, SomeClass, SomeClass)] = [
        makeContent(),
        makeContent(),
        makeContent()
    ]
    var array: [SomeClass] = .init()
    var closure: (() -> Void)?

    required init() { ... }
}

In constructor:

final class View {
    var set: Set<SomeClass>
    var optional: SomeClass?

    let simpleInitializer: SomeClass
    let verboseInitializer: SomeClass

    let tupleArray: [(SomeClass, SomeClass, SomeClass, SomeClass)]
    var array: [SomeClass]

    var closure: (() -> Void)?

    required init() {
        self.set = []

        self.simpleInitializer = .init()
        self.verboseInitializer = .init(
            arg1: 1,
            arg2: 2,
            arg3: 3,
            arg4: 4
        )

        self.tupleArray = [
            makeContent(),
            makeContent(),
            makeContent()
        ]
        self.array = .init()
    }
}

(* Types and namings are just virtual.)


So, I have many Swift files that goes like the first and cleaning/optimizing them up but facing a concern.

  • While the latter makes variable declarations simpler and easy to glance at (So I prefer), will there be consequences? More specifically, will there be dynamic (runtime) effect with the latter? (*In practice, some classes are inherited from NSView or NSObject, so Obj-C is also in concern.)

  • Or... is it just a matter of style of coding that does not worth mind?


With this simple test only difference I see is

main:
        xor     eax, eax
        ret

+ variable initialization expression of output.Test.value : Swift.Int:
+        xor     eax, eax
+        ret

Those 3 additional lines with in place initialization. Unfortunately I have no clue what this would mean in practice.
I imagine that not seeing ‘variable initialization expression’ here might mean dynamic assignment will be done, which is I'm not quite sure.


Any advices or suggestions are welcome, thanks in advance.

The "variable initialization expression" is a function that is called to initialise the stored variables. This mostly won't matter, but it can be a performance boundary in some edge cases. I wouldn't worry about it, you should make this choice stylistically.

3 Likes

Thanks for the input! So..

I forgot to mention that I have a feeling that inline declarations has advantages:
If you declare value immediately as RHS, then compiler statically know the resulting value so it would resolve a better optimization when building, because it doesn't need to set or find values in constructor dynamically (I'm not sure assigning values to stored properties in init is appropriate to describe as "dynamic", though). So that in the end, contributes to application performance as a result.

On the contrary, initializing stored properties in place is a 'feature' that requires extra call, powered by variable initialization expression (Although one should not worry about the extra calls normally).

Am I in the right direction?

No, this is false.

I wouldn't call a variable initialization expression an "extra call" because instance variables have to be initialized one way or another. There's no sense in which a variable initialization expression is "extra" compared to initializing the variable in the initializer.

1 Like

True. I'm not sure by my self why I am obsessed with this. :sweat_smile:
Seems it is just a styling as advised, did even Instrumenting and seeing no much difference.
Thanks.


edit: with SIL comparisons, the difference was also really only about "variable initialization expression" and @_hasInitialValue attibuttion. init remained the very same. Interesting!
For the future readers in case interested in:

struct InPlace {
  @_hasStorage @_hasInitialValue let value: Int { get }
  init()
}

...

// variable initialization expression of InPlace.value
sil hidden [transparent] @$s4main7InPlaceV5valueSivpfi : $@convention(thin) () -> Int {
bb0:
  %0 = integer_literal $Builtin.Int64, 0          // user: %1
  %1 = struct $Int (%0 : $Builtin.Int64)          // user: %2
  return %1 : $Int                                // id: %2
} // end sil function '$s4main7InPlaceV5valueSivpfi'

...
struct InConstructor {
  @_hasStorage let value: Int { get }
  init()
}

...