I have something that really puzzles me, specifically the following code triggers a compiler error "unresolved identifier self", and I am not sure why this is happening, as lazy means that at the time the property will be used, the class is already instantiated. Am I missing something?
Many thanks in advance.
Here is the code
class FirstClass {
unowned var second: SecondClass
init(second:SecondClass) {
self.second = second
print("First reporting for duty")
}
func aMethod() {
print("First's method reporting for duty")
}
}
class SecondClass {
lazy var first = FirstClass(second: self)
func aMethod() {
first.aMethod()
}
}
For some reason, a lazy property needs an explicit type annotation if its
initial value refers to self. This is mentioned on the swift-evolution mailing list, however I cannot explain why that is
necessary.
With
lazy var first: FirstClass = FirstClass(second: self)
// ^^^^^^^^^^^^
your code compiles and runs as expected.
Here is another example which demonstrates that the problem occurs
also with structs, i.e. it is unrelated to subclassing:
func foo(x: Int) -> Int { return x + 1 }
struct MyClass {
let x = 1
lazy var y = foo(0) // No compiler error
lazy var z1 = foo(self.x) // error: use of unresolved identifier 'self'
lazy var z2: Int = foo(self.x) // No compiler error
}
The initial value of y does not depend on self and does not need a
type annotation. The initial values of z1/z2 depend on self,
and it compiles only with an explicit type annotation.
Update: This has been fixed in Swift 4/Xcode 9 beta 3,
lazy property initializers can now reference instance members without explicit self, and without explicit type annotation. (Thanks to #hamish for the update.)
Related
Swift has this awesome error that shows up when you try to do something of the form x = x:
class Foo {
var foo = 1
}
var a = Foo()
a.foo = a.foo // error
This helped me avoid typos where the class had two similarly named properties, and I want to assign one to the other, but mistakenly typed the same one twice.
However, in this particular case:
struct Foo {
var foo = 1 {
didSet {
print("Did set")
}
}
mutating func f() {
foo = foo
}
}
var a = Foo()
a.foo = a.foo
It successfully compiles! There isn't even an error on the foo = foo line! If I change Foo to a class, or if I remove the didSet, then the expected error appears. It's just this struct + didSet combination that makes the compiler think "yeah, a.foo = a.foo makes a lot of sense! Let me allow that!"
I found this related post, which is about how to stop getting this error, rather than how to get it.
I also looked on bugs.swift.org, but there were only 3 results, and none of them are related.
I'm using Swift 5.3.2 and Xcode 12.4.
Is there any reason why assigning a struct property to itself is more "OK" than assigning a class property to itself?
Goal: Create a protocol that allows lazy computation of a property for structs that conform to the protocol and then add the properties for an array of those structs. The computation is intensive and should only be executed once, hence the lazy requirement.
So, after lots of reading (eg this: Swift Struct with Lazy, private property conforming to Protocol) and trial and error (please don't flag this as a duplicate, unless it actually addresses this exact case), I came up with something that works:
import Foundation
protocol Foo {
var footype: Double { mutating get }
func calculateFoo() -> Double
}
struct Bar: Foo {
private lazy var _footype: Double = {
let value = calculateFoo()
return value
}()
var footype: Double {
mutating get {
return _footype
}
}
func calculateFoo() -> Double {
print("calc")
return 3.453
}
}
Testing this in a Playground:
var bar = Bar()
print(bar.footype)
print(bar.footype)
And the output is:
calc
3.453
3.453
So far, so good.
Now, I want to make an array of Bar and add the footype properties:
var bar1 = Bar()
var bar2 = Bar()
var bar3 = Bar()
var bars = [bar1, bar2, bar3]
print(bars.map { $0.footype }.reduce(0.0, +))
Which gives me the following error:
Cannot use mutating getter on immutable value: '$0' is immutable
Found a lot of info on this error, but am stuck how to solve it. One way would be to use class instead of struct, but that doesn't work well with other parts of the code.
Is it possible what I am trying to achieve?
As you can tell from the error message, $0 is immutable, so you can't use a mutating member on it.
Therefore, you can't iterate over the bars directly. What you can do is iterating through its indices, because we know that bars[$0] is mutable:
print(bars.indices.map { bars[$0].footype }.reduce(0.0, +))
I have a generic class that I want to be able to use with a default type. Right now I can initialize it with any type, but I have to be explicit.
//Initialize with a type
MyManager<MyCustomerObject>()
// Initialize with NSObject (what I want to be my default type)
MyManager<NSObject>()
// This doesn't work, but I want this kind of functionality
class MyManager<T = NSObject> {}
// So I can create my manager like so and it inserts the default type as NSObject
MyManager() //Or MyManager<>()
Is this possible in Swift?
There's no support for default generic arguments, but you can fake it by defining the default init() on a type-constrained extension, in which case the compiler will be smart enough to use that type. E.g.:
class MyManager<T> {
let instance: T
init(instance: T) {
self.instance = instance
}
}
extension MyManager where T == NSObject {
convenience init() {
self.init(instance: NSObject())
}
}
And now you can initialize the type with no argument and it will default to MyManager<NSObject>:
let mm1 = MyManager(instance: "Foo") // MyManager<String>
let mm2 = MyManager(instance: 1) // MyManager<Int>
let mm3 = MyManager() // MyManager<NSObject>
SwiftUI uses this technique quite a lot.
No, this currently isn't possible – although it is a part of the Generics Manifesto, so might be something that the Swift team will consider for a future version of the language.
Default generic arguments
Generic parameters could be given the ability to provide default
arguments, which would be used in cases where the type argument is not
specified and type inference could not determine the type argument.
For example:
public final class Promise<Value, Reason=Error> { ... }
func getRandomPromise() -> Promise<Int, Error> { ... }
var p1: Promise<Int> = ...
var p2: Promise<Int, Error> = p1 // okay: p1 and p2 have the same type Promise<Int, Error>
var p3: Promise = getRandomPromise() // p3 has type Promise<Int, Error> due to type inference
In the meantime however, a somewhat unsatisfactory compromise would be the use of a typealias:
class MyManager<T> {}
typealias MyManagerDefault = MyManager<NSObject>
let defaultManager = MyManagerDefault()
Not nearly as slick as just being able to say MyManager(), but it does show up next to MyManager in auto-complete, which is pretty handy.
If T is always a NSObject subclass, you can use a generic constraint in Swift 5.3:
class MyManager<T: NSObject> {
let t = T()
}
class MyCustomerObject: NSObject { }
let a = MyManager()
let b = MyManager<MyCustomerObject>()
I have encountered what would seem like an extremely simple issue to solve, and I think I may be missing something.
The program crashes when attempting to create a recursive variable (?) with the typed to a class while being a member of the said class. For example:
class A {
var x: A
init() {
x = A()
}
}
Checking the crash log suggests a recursion of sorts, with init() being called infinitely.
Is there a proper method/syntax to solve this sort of problem, given that I need the variable x to be typed to class A, and the initializer must initialize variable x to class A?
It's obvious that at some step you should left property x uninitialized. So, thats better to declare it as Optional, and initialize it after instance was created:
class A {
var x: A?
}
let mainObject = A()
let nestedObject = A()
mainObject.x = nestedObject
Not sure but i think you are looking for this
class A {
var x: A?
init() {
}
anothermethod() {
x = A()
}
}
and you can call this method like
let a = A()
a.anothermethod()
Currently I've got some swift code like this:
class C {
let type: Type;
var num = 0;
init() {
self.type = Type({ (num: Int) -> Void in
self.num = num;
});
}
}
The Swift compiler refuses to permit it, saying that I've referenced self.type before it's initialized, even though that's clearly completely untrue. Furthermore, I can't employ the workaround found in other questions/answers, because the type is not optional, and it's immutable, so it can't be initialized with nil pointlessly first.
How can I make the Swift compiler accept this perfectly valid code?
This has nothing to do with returning from the initializer early. The callback is executed asynchronously- it is stored and then used later.
I also have a few further lets that are initialized after this one. I would have to turn them all into mutable optionals, even though they're not optional and can't be mutated.
This works:
class C {
var type: Type?;
var num = 0;
init() {
self.type = Type({ (num: Int) -> Void in
self.num = num;
});
}
}
I assume you knew that. But you want to know why your version isn't working.
Now for the tricky part: for the line
self.num = num;
to work, the compiler has to pass self to inside the closure. The closure could be and probably is executed inside of the constructor of Type.
This is as if you had written
self.type = Type({ (self: C, num: Int) -> Void in
self.num = num
});
which is syntactically wrong but explains what the compiler has to do to compile your code.
To pass this necessary instance of self to the constructor of Type, self has to be initialized. But self isn't initialized, because you are still in the constructor.
The compiler tells you which part of self is not initialized, when you try to pass self to the constructor of Type.
P.S.
obviously Type knows num in your code.
If you want to use let in C instead of var you could do...
class Type {
let num: Int
init () {
num = 3
}
}
class C {
let type: Type;
var num = 0;
init() {
self.type = Type();
num = type.num
}
}
or even
class C {
let type: Type;
var num: Int {
return type.num
}
init() {
self.type = Type();
}
}
depending on whether you want num to change or not. Both examples compile without error.
First, it's important to explain why this is not perfectly valid code, and that it isn't clear at all that self.type is not used before it is initialized. Consider the following extension of your code:
struct A {
init(_ f: (Int) -> Void) { f(1) }
}
class C {
let type: A
var num = 0 {
didSet { print(type) }
}
init() {
self.type = A({ (num: Int) -> Void in
self.num = num
})
}
}
If you walk through the logic, you'll note that self.type is accessed via print before it has been initialized. Swift can't currently prove that this won't happen, and so doesn't allow it. (A theoretical Swift compiler might prove that it wouldn't happen for some particular cases, but for most non-trivial code it would likely bump into the halting problem. In any case, the current Swift compiler isn't powerful enough to make this proof, and it's a non-trivial proof to make.)
One solution, though somewhat unsatisfying, is to use implicitly unwrapped optionals:
private(set) var type: A! = nil
Except for the declaration, every other part of the code is the same. You don't have to treat it as optional. In practice, this just turns off the "used before initialization" checks for this variable. It also unfortunately makes it settable inside of the current file, but does make it immutable to everyone else.
This is the technique I've most often used, though often I try to rework the system so that it doesn't require this kind of closure (not always possible, but I often rack my brain to try). It's not beautiful, but it is consistent and bounds the ugly.
Another technique that can work in some cases is laziness:
class C {
lazy var type: A = {
A({ (num: Int) -> Void in self.num = num })}()
var num = 0
init() {}
}
Sometimes that works, sometimes it doesn't. In your case it might. When it does work, it's pretty nice because it makes the property truly immutable, rather than just publicly immutable, and of course because it doesn't require !.
Interesting.
It looks like the error goes away if you avoid referencing self inside the closure.
If the callback is synchronous you can change your code as follow:
class C {
let type: Type
var num = 0
init() {
var numTemp = 0 // create a temporary local var
let initialType = Type({ (num: Int) -> () in
numTemp = num // avoid self in the closure
});
self.type = initialType
self.num = numTemp
}
}
Important: this will NOT work if the closure is async.
Tested with Xcode (Playground) 6.4 + Swift 1.2
Hope this helps.
As appzYourLife said, a temporary variable for num will suffice:
class Type{
var y: (Int)->Void
init(y2:((Int)->Void)){
self.y = y2
}
}
class C {
let type: Type
var num: Int = 0
init() {
var num2 = 0
self.type = Type(y2: { (num3: Int) -> () in
num2 = num3
});
self.num = num2
}
}
However, you do not need a temporary variable for type, this error message is misleading.