Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize lazy instance variable with value that depends on other instance variables

The following initialization currently produces this error in the line that calls getEventCalendar:

Cannot use instance member 'getEventCalendar' within property initializer; property initializers run before 'self' is available.

Is there any suitable way for initializing the lazy instance variable with a value that depends on other object-type instance variables of self (not just self alone) ? I've e.g. tried turning getEventCalendar from a method into a function, but this does not help either.

class AbstractEventCalendarClient {

  let eventStore: EKEventStore
  let entityType: EKEntityType

  lazy var eventCalendar = getEventCalendar()

  init(eventStore: EKEventStore, entityType: EKEntityType) {
    self.eventStore = eventStore
    self.entityType = entityType
  }

  func getEventCalendar() -> EKCalendar? {
    // ...
  }
}
like image 919
Drux Avatar asked Oct 05 '16 07:10

Drux


People also ask

How many ways can you initialize an instance variable?

Normally, you would put code to initialize an instance variable in a constructor. There are two alternatives to using a constructor to initialize instance variables: initializer blocks and final methods. The Java compiler copies initializer blocks into every constructor.

Are instance variables automatically initialized?

Instance variables of numerical type (int, double, etc.) are automatically initialized to zero if you provide no other values; boolean variables are initialized to false; and char variables, to the Unicode character with code number zero. An instance variable can also be a variable of object type.

How do you declare a lazy variable?

You indicate a lazy stored property by writing the lazy modifier before its declaration. You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes.

What is used to initialize instance variable?

A class contains a constructor to initialize instance variables in Java. This constructor is called when the class object is created.


2 Answers

You can use a once-only executed closure which captures properties of self and use these at execution (= first use of the lazy property). E.g.

class Foo {
    var foo: Int
    var bar: Int
    lazy var lazyFoobarSum: Int = { return self.foo + self.bar }()

    init(foo: Int, bar: Int) { 
        self.foo = foo 
        self.bar = bar
    }
}

let foo = Foo(foo: 2, bar: 3)
foo.foo = 7
print(foo.lazyFoobarSum) // 10

W.r.t. to your own attempt: you may, in the same way, make use of help (instance) functions of self in this once-only executed closure.

class Foo {
    var foo: Int
    var bar: Int
    lazy var lazyFoobarSum: Int = { return self.getFooBarSum() }()

    init(foo: Int, bar: Int) { 
        self.foo = foo 
        self.bar = bar
    }

    func getFooBarSum() -> Int { return foo + bar }
}

let foo = Foo(foo: 2, bar: 3)
foo.foo = 7
print(foo.lazyFoobarSum) // 10
like image 125
dfrib Avatar answered Oct 06 '22 05:10

dfrib


It's a confusing error message (which you may well want to file a bug report on). The problem is just a quirk of lazy properties – they currently require an explicit use of self in order to access instance members, as well as an explicit type annotation when doing so (which has been previously noted in this Q&A).

Therefore you need to say:

lazy var eventCalendar: EKCalendar? = self.getEventCalendar()
like image 28
Hamish Avatar answered Oct 06 '22 05:10

Hamish