Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift weak lazy variable won't compile

To demonstrate this problem, I made a vanilla Cocoa project. Here is the AppDelegate.swift:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    weak lazy var isGood : NSNumber? = {
        return true
    }()

    func doSomething() {
        let result = isGood!
    }

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // Insert code here to initialize your application
    }

    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }
}

Xcode gives this:

unkown :0: error: cannot convert return expression of type 'NSNumber?' (aka 'Optional') to return type 'NSNumber?'

unkown :0: cannot assign value of type 'NSNumber?' to type 'NSNumber??'

unkown :0: cannot assign value of type 'NSNumber?' to type 'NSNumber??'

In my actual project, it's another object of MyCustomClass (instead of NSNumber). The error is the same except the type is MyCustomClass.

If I remove weak or lazy from the declaration, it's all fine. But I wanted to save the reference count from being +1, since the MyCustomClass is an NSViewController which is sure to always be there.

Any idea of how to use the weak lazy variable?

like image 863
LShi Avatar asked Jul 03 '16 16:07

LShi


3 Answers

try to use a weak computed property instead ...

import Foundation

class C {
    weak var d : NSDate? {
        return NSDate()
    }
}

let c = C()
print(NSDate())
sleep(1)
print(c.d)

which give you the wanted behaviour (if i understand you question :-))

2016-07-03 16:49:04 +0000
Optional(2016-07-03 16:49:05 +0000)

if d MUST be initialized only once, then

import Foundation

class C {
    weak var d : NSDate? {
        return d0
    }
    lazy var d0: NSDate? = {
        return NSDate()
    }()
}

let c = C()
print(NSDate())
sleep(1)
print(c.d)
sleep(1)
print(c.d)

which gives you exactly what you need

2016-07-03 16:57:24 +0000
Optional(2016-07-03 16:57:25 +0000)
Optional(2016-07-03 16:57:25 +0000)
like image 105
user3441734 Avatar answered Nov 01 '22 21:11

user3441734


Weak and lazy do not mix well. The error message is completely useless at explaining what is going on, but essentially lazy and weak are at odds with each other:

  • lazy tells Swift that you don't want your variable created until the first time you access it, but once it is created, you want to keep it indefinitely for future reference, while
  • weak tells Swift that you don't want your variable to be the last link that keeps your variable from being deallocated, which works against the "keep indefinitely" goal of lazy variables.

You can solve this by emulating lazy, like this:

class Foo {

    weak var isGoodCache : NSNumber?

    private var makeIsGood : NSNumber {
        isGoodCache = true
        return isGoodCache!
    }

    var isGood:NSNumber? {
        return isGoodCache ?? makeIsGood
    }
}
like image 35
Sergey Kalinichenko Avatar answered Nov 01 '22 21:11

Sergey Kalinichenko


The reason why lazy and weak are incompatible is that every use would create a new instance unless you have another place that holds a strong reference. If you have another place to hold a strong reference then you don't need your class member to be the one creating the instance.

Lets assume we have:

weak lazy var myVariable: MyClass? = createMyClassInstance()

When you use it the first time, lets say you just reference it in a function call somewhere ...

myFunction(myVariable)

on the very next line, myVariable is empty again.

Why is that? because, once myFunction is finished, there are no more references to myVariable, and since it is weak, the corresponding object goes out of scope and vanishes.

There would be no difference between that lazy weak variable and any function call result. So, your variable may as well be a function or a computed variable (which would make things clearer for anybody looking at your code).

like image 8
Alain T. Avatar answered Nov 01 '22 23:11

Alain T.