lazy initialisation


(J.E. Schotsman) #1

Hello,

I need to initialize a variable of a class with a closure using the value of some variables of the class.
Since this is not permitted I thought I might solve the problem by declaring it lazy. But this is still rejected by the compiler.
If there are no circular dependencies in initialization of lazy variables this could be allowed, couldn’t it?
Or is this a very big if?

My workaround is ugly: a calculated property + a private variable + an initialized:Bool variable.

Jan E.


(Mark Dalrymple) #2

This works for me

class Blorg: NSObject, URLSessionDelegate {

    var config: URLSessionConfiguration

    lazy var session: URLSession = {

        print("howdy")

        return URLSession(configuration: self.config, delegate: self,
delegateQueue: nil)

    }()

    override init() {

        config = URLSessionConfiguration.default()

    }

}

Cheers,
++md

···

On Mon, Jul 4, 2016 at 1:04 PM, J.E. Schotsman via swift-users < swift-users@swift.org> wrote:

Hello,

I need to initialize a variable of a class with a closure using the value
of some variables of the class.
Since this is not permitted I thought I might solve the problem by
declaring it lazy. But this is still rejected by the compiler.
If there are no circular dependencies in initialization of lazy variables
this could be allowed, couldn’t it?
Or is this a very big if?

My workaround is ugly: a calculated property + a private variable + an
initialized:Bool variable.

Jan E.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Zhao Xin) #3

You'd better sharing some of you code here first.

Zhaoxin

···

On Tue, Jul 5, 2016 at 1:04 AM, J.E. Schotsman via swift-users < swift-users@swift.org> wrote:

Hello,

I need to initialize a variable of a class with a closure using the value
of some variables of the class.
Since this is not permitted I thought I might solve the problem by
declaring it lazy. But this is still rejected by the compiler.
If there are no circular dependencies in initialization of lazy variables
this could be allowed, couldn’t it?
Or is this a very big if?

My workaround is ugly: a calculated property + a private variable + an
initialized:Bool variable.

Jan E.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Mark Dalrymple) #4

Here's the one I started with:

   lazy var c:Int = {return a*b}()

and ended up with:

    lazy var c:Int = {return self.a * self.b}()

It's in a closure, so need to explicitly reference self.

Cheers,
++md

···

On Mon, Jul 4, 2016 at 3:04 PM, J.E. Schotsman via swift-users < swift-users@swift.org> wrote:

> On 04 Jul 2016, at 19:21, Zhao Xin <owenzx@gmail.com> wrote:
>
> You'd better sharing some of you code here first.

For example, consider this:

class TestStruct1
        {
        let a = 10
        let b = 20
        let c:Int = {return self.a*self.b}()
        }

Of course this is a trivial example. In reality the calculation of c from
a and b might take longer.

Since this is not allowed I try

struct TestStruct2
        {
        let a = 10
        let b = 20
        lazy var c:Int = {return a*b}()
        }

Not allowed either even though neither a nor b is lazy.
I have to do

struct TestStruct3
        {
        let a = 10
        let b = 20

        private var cInitialized = false
        private var _c = 0
        var c:Int
         {
         mutating get {
        if !cInitialized
                {
                _c = a*b
                cInitialized = true
                }
         return _c }
         }
        }

BTW I pasted Mark’s code in a playground and it compiles indeed.
What’s the difference?

Jan E.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(J.E. Schotsman) #5

For example, consider this:

class TestStruct1
  {
  let a = 10
  let b = 20
  let c:Int = {return self.a*self.b}()
  }

Of course this is a trivial example. In reality the calculation of c from a and b might take longer.

Since this is not allowed I try

struct TestStruct2
  {
  let a = 10
  let b = 20
  lazy var c:Int = {return a*b}()
  }

Not allowed either even though neither a nor b is lazy.
I have to do

struct TestStruct3
  {
  let a = 10
  let b = 20

  private var cInitialized = false
  private var _c = 0
  var c:Int
   {
   mutating get {
  if !cInitialized
    {
    _c = a*b
    cInitialized = true
    }
   return _c }
   }
  }

BTW I pasted Mark’s code in a playground and it compiles indeed.
What’s the difference?

Jan E.

···

On 04 Jul 2016, at 19:21, Zhao Xin <owenzx@gmail.com> wrote:

You'd better sharing some of you code here first.


(J.E. Schotsman) #6

Oh of course, I got tripped up by the similarity of TestStruct1 and TestStruct2.
Problem solved.

BTW this compiles:

      struct TestStruct2
        {
        lazy var c:Int = {return self.d}()
        lazy var d:Int = {return self.c}()
        }

and causes a stack overflow:

var x = TestStruct2()
let y = x.c

···

On 04 Jul 2016, at 21:12, Mark Dalrymple <markd@borkware.com> wrote:

lazy var c:Int = {return self.a * self.b}()

It's in a closure, so need to explicitly reference self.


(Karl) #7

I’ve been hitting this myself. I think it’s a bug -- yes, you are technically in a closure, but the closure does not capture ‘self’ beyond the lifetime of ‘self’. In this context, ’self’ should automatically be ‘unowned’.

Karl

···

On 4 Jul 2016, at 21:12, Mark Dalrymple via swift-users <swift-users@swift.org> wrote:

Here's the one I started with:

   lazy var c:Int = {return a*b}()

and ended up with:

    lazy var c:Int = {return self.a * self.b}()

It's in a closure, so need to explicitly reference self.

Cheers,
++md


(Zhao Xin) #8

No, it is not a bug.

For a closure, you have to call self explicitly unless the closure is mark
as @noescape. Also, in this situation, self is not unowned, as the closure
is not stored, it ran and released. Below, is a situation that you need use
unowned self. Here the closure is stored in variable d instead of running
and releasing.

    lazy var d:()->Int = { [unowned self] in
        return self.a*self.b
    }

Zhaoxin

···

On Tue, Jul 5, 2016 at 3:42 AM, Karl via swift-users <swift-users@swift.org> wrote:

On 4 Jul 2016, at 21:12, Mark Dalrymple via swift-users < > swift-users@swift.org> wrote:

Here's the one I started with:

   lazy var c:Int = {return a*b}()

and ended up with:

    lazy var c:Int = {return self.a * self.b}()

It's in a closure, so need to explicitly reference self.

Cheers,
++md

I’ve been hitting this myself. I think it’s a bug -- yes, you are
technically in a closure, but the closure does not capture ‘self’ beyond
the lifetime of ‘self’. In this context, ’self’ should automatically be
‘unowned’.

Karl

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Karl) #9

In this specific case, when you are initialising from a closure, there is no need to make the capture of ‘self’ explicit and it’s totally safe for it to be unowned. You can’t invoke the closure without going through a valid instance, and that instance always owns the closure and never the other way around.

···

On 5 Jul 2016, at 03:47, Zhao Xin <owenzx@gmail.com> wrote:

No, it is not a bug.

For a closure, you have to call self explicitly unless the closure is mark as @noescape. Also, in this situation, self is not unowned, as the closure is not stored, it ran and released. Below, is a situation that you need use unowned self. Here the closure is stored in variable d instead of running and releasing.

    lazy var d:()->Int = { [unowned self] in
        return self.a*self.b
    }

Zhaoxin


(Zhao Xin) #10

The compiler is not smart enough to treat this as you think, nor it will be
designed to. According to the documents, it is the developer‘s burden ​to
add @noescape or weak or unowned. So I disagree it is a bug.

Zhaoxin

···

On Sat, Jul 9, 2016 at 7:30 AM, Karl <razielim@gmail.com> wrote:

On 5 Jul 2016, at 03:47, Zhao Xin <owenzx@gmail.com> wrote:

No, it is not a bug.

For a closure, you have to call self explicitly unless the closure is mark
as @noescape. Also, in this situation, self is not unowned, as the closure
is not stored, it ran and released. Below, is a situation that you need use
unowned self. Here the closure is stored in variable d instead of running
and releasing.

    lazy var d:()->Int = { [unowned self] in
        return self.a*self.b
    }

Zhaoxin

In this specific case, when you are initialising from a closure, there is
no need to make the capture of ‘self’ explicit and it’s totally safe for it
to be unowned. You can’t invoke the closure without going through a valid
instance, and that instance always owns the closure and never the other way
around.


(Karl) #11

I know that the compiler doesn’t do this today, but I disagree that it will never have enough information to make inferences like this. It would simply be an adaptation of ARC to Swift - you don’t have these kind of attached lazy closures in Obj-C, so there was never any need for it. They run in the context of the instance just like a getter would, so they should have the same properties as a getter (including implicit ‘self’).

In general though, I think we are moving to “property behaviours”, which may need something like this in general. You would want to use unowned references in a stored property behaviour object; any retains/releases would be unnecessary. I’m sure we’ll talk about it more when that gets further along.

Karl

···

On 9 Jul 2016, at 06:34, Zhao Xin <owenzx@gmail.com> wrote:

The compiler is not smart enough to treat this as you think, nor it will be designed to. According to the documents, it is the developer‘s burden ​to add @noescape or weak or unowned. So I disagree it is a bug.

Zhaoxin

On Sat, Jul 9, 2016 at 7:30 AM, Karl <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 5 Jul 2016, at 03:47, Zhao Xin <owenzx@gmail.com <mailto:owenzx@gmail.com>> wrote:

No, it is not a bug.

For a closure, you have to call self explicitly unless the closure is mark as @noescape. Also, in this situation, self is not unowned, as the closure is not stored, it ran and released. Below, is a situation that you need use unowned self. Here the closure is stored in variable d instead of running and releasing.

    lazy var d:()->Int = { [unowned self] in
        return self.a*self.b
    }

Zhaoxin

In this specific case, when you are initialising from a closure, there is no need to make the capture of ‘self’ explicit and it’s totally safe for it to be unowned. You can’t invoke the closure without going through a valid instance, and that instance always owns the closure and never the other way around.