unowned self in closure in a playground


(Ray Fix) #1

Hi,

The following code crashes:

class Demo {
  var value = 0
  lazy var increment: (Int) -> Void = { [unowned self] by in
    self.value += by
    print(self.value)
  }
}

Demo().increment(3)
error: Playground execution aborted: error: Execution was interrupted, reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

However if I call it this way:

do {
  let demo = Demo()
  demo.increment(3)
}

All is well. This breaks my mental model of lifetime guaranteed to be around until the method call completes.
Is it me that is wrong or the playground. Is the second way working just by luck?


(Marco S Hyman) #2

The following code crashes:

class Demo {
  var value = 0
  lazy var increment: (Int) -> Void = { [unowned self] by in
    self.value += by
    print(self.value)
  }
}

Demo().increment(3)
error: Playground execution aborted: error: Execution was interrupted, reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

value is not a static/class variable. Without an instance of Demo it does not exist. This seems to work.

class Demo {
  static var value = 0
  lazy var increment: (Int) -> Void = { [unowned self] by in
    value += by
    print(value)
  }
}


#3

lifetime guaranteed to be around until the method call completes

AFAIK, this is true.
However, in this case, it's not a method call. The lifetime is only
guaranteed until getter:increment completes.

Demo().increment(3)

···

--------------| lifetime

2017-02-22 3:00 GMT+09:00 Ray Fix via swift-users <swift-users@swift.org>:

Hi,

The following code crashes:

class Demo {
  var value = 0
  lazy var increment: (Int) -> Void = { [unowned self] by in
    self.value += by
    print(self.value)
  }
}

Demo().increment(3)
error: Playground execution aborted: error: Execution was interrupted,
reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

However if I call it this way:

do {
  let demo = Demo()
  demo.increment(3)
}

All is well. This breaks my mental model of lifetime guaranteed to be
around until the method call completes.
Is it me that is wrong or the playground. Is the second way working just
by luck?

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


(Ray Fix) #4

Thanks. My comment below.

The following code crashes:

class Demo {
var value = 0
lazy var increment: (Int) -> Void = { [unowned self] by in
   self.value += by
   print(self.value)
}
}

Demo().increment(3)
error: Playground execution aborted: error: Execution was interrupted, reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

value is not a static/class variable. Without an instance of Demo it does not exist. This seems to work.

class Demo {
static var value = 0
lazy var increment: (Int) -> Void = { [unowned self] by in
   value += by
   print(value)
}
}

The problem with this solution is that this changes the semantics. I want a value for every Demo instance.

Ray

···

On Feb 21, 2017, at 11:18 AM, Marco S Hyman <marc@snafu.org> wrote:


(Ray Fix) #5

Thanks! This is an interesting explanation.

It is curious that naming the variable cases different behavior.
IOW, this doesn’t have a problem:

let demo = Demo()
demo.increment(3)

Lifetime seems to last the entire expression.

Ray

···

On Feb 21, 2017, at 7:32 PM, rintaro ishizaki <fs.output@gmail.com> wrote:

lifetime guaranteed to be around until the method call completes

AFAIK, this is true.
However, in this case, it's not a method call. The lifetime is only guaranteed until getter:increment completes.

Demo().increment(3)
>--------------| lifetime


(Zhao Xin) #6

I think you are right on ` lifetime guaranteed to be around until the
method call completes`. But`[unowned self]` released the retain manually in
your code. Just removing `[unowned self]` part, you code will work.

Zhaoxin

···

On Wed, Feb 22, 2017 at 3:18 AM, Marco S Hyman via swift-users < swift-users@swift.org> wrote:

> The following code crashes:
>
> class Demo {
> var value = 0
> lazy var increment: (Int) -> Void = { [unowned self] by in
> self.value += by
> print(self.value)
> }
> }
>
> Demo().increment(3)
> error: Playground execution aborted: error: Execution was interrupted,
reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

value is not a static/class variable. Without an instance of Demo it
does not exist. This seems to work.

class Demo {
  static var value = 0
  lazy var increment: (Int) -> Void = { [unowned self] by in
    value += by
    print(value)
  }
}

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


#7

lifetime guaranteed to be around until the method call completes

AFAIK, this is true.
However, in this case, it's not a method call. The lifetime is only
guaranteed until getter:increment completes.

Demo().increment(3)
>--------------| lifetime

Thanks! This is an interesting explanation.

It is curious that naming the variable cases different behavior.
IOW, this doesn’t have a problem:

let demo = Demo()
demo.increment(3)

Lifetime seems to last the entire expression.

I'm not sure, but I think the compiler is just lazy enough to release the "
demo" value *after* the entire expression or scope finishes.
Again, I'm not sure. :slight_smile:

Anyway, If I were you, I would define a wrapper method:

class Demo {
  var value = 0

  lazy var _increment: (Int) -> Void { [unowned self] by in ... }
  func increment(by: Int) {
    _increment(by)
  }
}
Demo().increment(by: 3)

···

2017-02-22 12:53 GMT+09:00 Ray Fix <rayfix@gmail.com>:

On Feb 21, 2017, at 7:32 PM, rintaro ishizaki <fs.output@gmail.com> wrote:

Ray


(Marco S Hyman) #8

The problem with this solution is that this changes the semantics. I want a value for every Demo instance.

My error... I read too fast and missed the () instantiation of Demo in the failing call. I was reading it as Demo.increment().

Never mind.