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.
varinstead oflet. e.g.var resultlazy. e.g.lazy result: CGFloat = { return 0 }- make
calculate()to beclass/staticor 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.