class Foo {
let result: CGFloat
init() {
result = calculate()
}
private func calculate() -> CGFloat {
// do many complex calculation with many codes
return 0
}
}
There is no doubt that the error occurs.
'self' used in method call 'calculate' before all stored properties are initialized`
I know several ways to solve this problem.
var
instead oflet
. e.g.var result
lazy
. e.g.lazy result: CGFloat = { return 0 }
- make
calculate()
to beclass/static
or global function. e.gstatic func calculate()
.
But I think that's not what I want.
Why let
let
means immutable. Though the calculation of result
may be complex, but it's truly immutable. So var
is not the best practice.
Why calculate()
Too many codes in init()
is not idiomatic and annoying
Why not class/static
Other instance properties can not be used in static function.
Update
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
let result: CGFloat
init() {
foo3 = foo1 * foo2
result = calculate()
}
private func calculate() -> CGFloat {
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
}
For more clear, I add another snippet. In fact result
is the same with foo3
. So why foo3 = foo1 * foo2
, but result = calculate()
?
That's because the calculation of result
(maybe 10 lines codes) is a little more complex than the calculation of foo3
(just one lines). If I put all these codes in init()
, it will be messy.
Try all ways mentioned
1. var
instead of let
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
var result: CGFloat
init() {
foo3 = foo1 * foo2
result = calculate()
}
private func calculate() -> CGFloat {
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
}
It works, but result
is not immutable.
2. lazy
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
lazy var result: CGFloat = {
calculate()
}()
init() {
foo3 = foo1 * foo2
}
private func calculate() -> CGFloat {
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
}
It works, but result
is also not immutable.
3. static function
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
let result: CGFloat
init() {
foo3 = foo1 * foo2
result = Foo.calculate()
}
static private func calculate() -> CGFloat {
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
}
Build fails,
Instance member 'foo3' cannot be used on type 'Foo'
4. closure
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
let result: CGFloat
private let calculate = { () -> CGFloat in
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
init() {
foo3 = foo1 * foo2
result = calculate()
}
}
Build fails,
Instance member 'foo3' cannot be used on type 'Foo'
5. computing property
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
var result: CGFloat {
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
init() {
foo3 = foo1 * foo2
}
}
It works, but result is 'var' and will be calculated for each usage.
6. tool class
class Foo {
let foo1: CGFloat = 1.0
let foo2: CGFloat = 2.0
let foo3: CGFloat
let result: CGFloat
init() {
foo3 = foo1 * foo2
result = Tool.calculate(foo3: foo3)
}
}
class Tool {
static func calculate(foo3: CGFloat) -> CGFloat {
// do some calculation
let constants: CGFloat = 100
// (10 lines...)
return foo3 + constants
}
}
It works, but we bring in a Tool
class.
Summary
1, 2, 6 is appropriate. 6 is suit for complex calculation.
Extension
"Implicitly unwrapped optionals" is mentioned several time. I confused why until see this answer.