It seems that it's not possible to implement a variable that's required by a protocol, with a lazy variable. For example:
protocol Foo {
var foo: String { get }
}
struct Bar: Foo {
lazy var foo: String = "Hello World"
}
Compiler complains that Type 'Bar' does not conform to protocol 'Foo'.
It's also not possible to add the lazy keyword in the protocol declaration, since then you get 'lazy' isn't allowed on a protocol requirement error.
So is this not at all possible?
Citing the Language Guide - Properties - Lazy Stored Properties [emphasis mine]:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used.
I.e., the value is mutated upon first usage. Since foo has been blueprinted in the Foo protocol as get, implicitly nonmutating get, the value type Bar does not fulfil this promise with its lazy property foo, a property with a mutating getter.
Changing Bar to a reference type will allow it to fulfil the Foo blueprint (as mutating a property of a reference type doesn't mutate the type instance itself):
protocol Foo {
var foo: String { get }
}
class Bar: Foo {
lazy var foo: String = "Hello World"
}
Alternative, specify in the blueprint of the foo property of Foo that it has a mutating getter.
protocol Foo {
var foo: String { mutating get }
}
struct Bar: Foo {
lazy var foo: String = "Hello World"
}
See the following Q&A for some additional details of the mutating/nonmutating specifiers for getters and setters:
Swift mutable set in property
Related
Compiles:
struct Foo {
var g: Double = 5.0
}
struct Bar {
var h: Double = 5.0
var foo = Foo()
}
var bar = Bar(h: 6)
Add "private" and no longer compiles, with above error in title:
struct Foo {
var g: Double = 5.0
}
struct Bar {
var h: Double = 5.0
private var foo = Foo()
}
var bar = Bar(h: 6) // compiler error on this line
Why would that be?
This is because for a struct a synthesised memberwise init will be private if any of its members are private. So instead you will get an init without arguments since all properties has default values (if at least one property that is non-optional lacks a default value no init will be synthesised at all).
From the Swift Programming Language book
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Likewise, if any of the structure’s stored properties are file private, the initializer is file private. Otherwise, the initializer has an access level of internal.
So in your case you will need to write a custom init for your struct
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, +))
When defining a class in Swift, you can have var properties which are like normal fields in other OOP languages, but also let properties which are both read-only and immutable (like T const * const in C++).
However is there a Swift equivalent of C++'s T * const? (That is, the field itself is immutable, but the object it points to is mutable)?
Here's a representation of my scenario:
class Foo {
let bar: Bar
init(bar: Bar) {
self.bar = bar
}
}
protocol Bar {
var fleem: Int? { get set }
}
class ConcreteBar : Bar {
var fleem: Int? = nil
}
var foo: Foo = Foo( bar: ConcreteBar() )
foo.bar.fleem = 123
(Playground link: https://iswift.org/playground?3jKAiu&v=2 )
Presently this gives me this compiler error:
Swift:: Error: cannot assign to property: 'bar' is a 'let' constant`
foo.bar.fleem = 123
Note that I am not actually setting bar, I'm only setting bar.fleem. I don't know why the compiler is complaining about assigning to bar.
If I change Foo to use this:
class Foo {
var bar: Bar
// ...
...then it compiles okay, but then I lose the guarantee that Foo.bar always has the same instance.
I know I could also change it to private(set):
class Foo {
public private(set) var bar: Bar
// ...
...but Foo itself is still free to overwrite the bar object-reference, and the use of var means that the compiler cannot assume the reference is immutable either, so some optimizations may be skipped.
I'm looking for something like a hypothetical let mutable or var readonly keyword or modifier.
By default, protocol typed objects have value value semantics. As a consequence, they're not mutable if the variable is a let constant.
To introduce reference semantics (and by extension, the mutability of objects referred to be a let constant), you need to make your protocol into a class protocol:
protocol Bar: class {
var fleem: Int? { get set }
}
You need to add the class attribute to the protocol to make it reference type compliant:
protocol Bar : class { ...
Swift inherited Objective-C's metaclass concept: classes themselves are also considered objects. A class Foo's object's class is Foo.self, and it is of type Foo.Type. If Foo inherits from Bar, then Foo.self can be assigned to a variable of type Bar.Type, too. This has at least two benefits:
it allows to override "static methods";
it's easy to create an instance of an unknown class in a type-safe way and without using reflection.
I'm particularly looking at the second one right now. Just to be sure that everybody understands what I'm after, here's an example:
class BaseFoo {
var description: String { return "BaseFoo" }
}
class DerivedFoo: BaseFoo {
override var description: String { return "DerivedFoo" }
}
let fooTypes: [BaseFoo.Type] = [BaseFoo.self, DerivedFoo.self] // metaclass magic!
for type in fooTypes {
let object: BaseFoo = type() // metaclass magic!
println(object)
}
Now, I have an array of AnyClass objects (any metaclass instance can be assigned to AnyClass, just like any object can be assigned to AnyObject), and I want to find which ones implement a given protocol. The protocol would declare an initializer, and I would instantiate the class just like I do in the example above. For instance:
protocol Foo {
init(foo: String)
}
class Bar: Foo {
required init(foo: String) { println("Bar initialized with \(foo)") }
}
class Baz {
required init() { println("I'm not a Foo!") }
}
let types: [AnyClass] = [Bar.self, Baz.self]
So far so good. Now, the problem is determining if the class implements the protocol. Since metaclass instances are polymorphic, I'd expect to be able to cast them. However, I'm apparently missing something, because Swift won't let me write this:
for type in types {
if let fooType = type as? Foo.Type {
let obj = fooType(foo: "special snowflake string")
}
}
The compiler error I get is:
error: 'Foo' is not identical to 'AnyObject'
Is there any way to determine if a metaclass instance represents a class that implements a protocol, and is there any way to cast that instance into a protocol type?
I tried to declare Foo as a class protocol, but it's apparently not enough.
EDIT: I just tried with the Any type, and while it doesn't cause a syntax error, it crashes the Swift compiler.
As of Xcode 7 beta 2 and Swift 2 it has been fixed. You can now write:
for type in types {
if let fooType = type as? Foo.Type {
// in Swift 2 you have to explicitly call the initializer of metatypes
let obj = fooType.init(foo: "special snowflake string")
}
}
Or if you only want type as type Foo.Type you can use for case
for case let type as Foo.Type in types {
let obj = type.init(foo: "special snowflake string")
}
In swift both the Array and Dictionary classes have different implementations depending on whether you declare them variable or constant. My question is can this type of functionality be used on classes you define, or is this reserved for Array, Dictionary?
The implementation of Array and Dictionary are not different for variables and constants -- instead, mutating methods (methods that change the value of any part of a struct) are only callable for variables (declared with var) but not callable for constants (declared with let).
That is to say:
struct MyType {
var name: String
func capitalizedName() -> String {
return name.capitalized()
}
mutating func setName(newName: String) {
self.name = newName
}
}
var myVariable = MyType(name: "Foo")
myVariable.setName("Bar")
println(myVariable.capitalizedName()) // print BAR
let myConstant = MyType(name: "Baz")
// not allowed by compiler:
// myConstant.setName("Nope!")