Inner didSet protection bizarrely extends to the whole class? - swift

It's well-known that, of course, didSet will not run on the same object again from inside a didSet. (example.)
However. It seems that: the restriction applies not only to that object, but to maybe any object of the same class.
Here are copy-paste test cases for Playground.
class C {
var Test: Bool = false {
didSet {
print("test.")
for c in r {
c.Test = true
}
}
}
var r:[C] = []
}
var a:C = C()
var b:C = C()
var c:C = C()
a.r = [b, c]
a.Test = false
Does not work!
class C {
var Test2: Bool = false {
didSet {
print("test2.")
global.Test2 = true
}
}
}
var global:C = C()
var a:C = C()
a.Test2 = false
Does not work!
Is this a Swift bug?
If not, what is the actual restriction? It won't run ANY didSet (whatsoever) that starts from a didSet?; the same identical class?; the same super class?; or?
Where exactly is this explained in the doco?
WTF. One needs to know ... what is the actual restriction specifically?

This is bug SR-419.
From the comment on the bug:
Ugh. We really need to check that the base of the property access is statically self.
and from my experiments it seems that the didSet observer is not invoked only if you set the same property on any object. If you set any other property (even on the same object), the observer is invoked correctly.
class A {
var name: String
var related: A?
var property1: Int = 0 {
didSet {
print("\(name), setting property 1: \(property1)")
self.property2 = 100 * property1
related?.property1 = 10 * property1
related?.property2 = 100 * property1
}
}
var property2: Int = 0 {
didSet {
print("\(name), setting property 2: \(property2)")
}
}
init(name: String) {
self.name = name
}
}
let a = A(name: "Base")
a.related = A(name: "Related")
a.property1 = 2
Output:
Base, setting property 1: 2
Base, setting property 2: 200
Related, setting property 2: 200
when the expected output should be:
Base, setting property 1: 2
Base, setting property 2: 200
Related, setting property 1: 20
Related, setting property 2: 2000
Related, setting property 2: 200
It seems you also need to assign that property directly from the observer. Once you enter another function (or observer), the observers start working again:
var property1: Int = 0 {
didSet {
print("\(name), setting property 1: \(property1)")
onSet()
}
}
...
func onSet() {
self.property2 = 100 * property1
related?.property1 = 10 * property1
related?.property2 = 100 * property1
}
And that is the best workaround.
Another workaround (thanks #Hamish) is to wrap nested assignments into an immediately executed closure:
var property1: Int = 0 {
didSet {
{
self.property2 = 100 * property1
related?.property1 = 10 * property1
related?.property2 = 100 * property1
}()
}
}
Depending on code before the closure, you might have to wrap it into parenthesis or insert a semicolon after the preceding statement.

Related

Cannot assign to immutable expression of type ... but with protocols

I know there are lots of questions starting like that. However, here's my code snippet:
protocol AProt { var a:Int { get set } }
protocol BProt { var b:Int { get set } }
protocol CProt { var c:Int { get set } }
class A:AProt, CProt { var a = 1; var c = 3 }
class B:BProt, CProt { var b = 2; var c = 30 }
var a = A()
var b = B()
var c = a as CProt // works
c.c = 123
a = c as! A
print (a.c)
(a as CProt).c = 999 // throws error
print (a.c)
Looking at this answer it tells that the (...as...) makes the expression immutable. But ...
while the (a as CProt).c = 999 fails with above error message, the work around with the var c = a as CProt works. I'd regard this as simple compiler bug, but I wanted to know whether there's something that could be done here (like adding a secret key word).
This isn't a compiler bug. CProt can be a value type. Consider this code:
protocol CProt { var c:Int { get set } }
struct A: CProt { var a = 1; var c = 3 } // Note this is a struct
var a = A() // It's mutable
let c = a as CProt // But this one isn't
c.c = 999 // So no surprise this fails
Would you expect this to work? It shouldn't, since c is an immutable struct. This is the same thing you're writing in (a as CProt).c = 999.
The problem is you expect CProt to be a reference type. If you need that, then say so:
protocol CProt:class { var c:Int { get set } } // Note the addition of :class
class A: CProt { var a = 1; var c = 3 }
let a = A()
(a as CProt).c = 999 // Success

Read-Only properties

I need help with "read-only" in swift. I tried various ways, but simply couldn't figure out how to compile it without errors. Here's the question and what i thought of.
Create a read-only computed property named isEquilateral that checks to see whether all three sides of a triangle are the same length and returns true if they are and false if they are not.
var isEquilateral: Int {
}
If you want a "read-only" stored property, use private(set):
private(set) var isEquilateral = false
If it is a property calculated from other properties, then, yes, use computed property:
var isEquilateral: Bool {
return a == b && b == c
}
For the sake of completeness, and probably needless to say, if it is a constant, you’d just use let:
let isEquilateral = true
Or
struct Triangle {
let a: Double
let b: Double
let c: Double
let isEquilateral: Bool
init(a: Double, b: Double, c: Double) {
self.a = a
self.b = b
self.c = c
isEquilateral = (a == b) && (b == c)
}
}
Something like this? (as suggested by #vacawama in the comments)
struct Triangle {
let edgeA: Int
let edgeB: Int
let edgeC: Int
var isEquilateral: Bool {
return (edgeA, edgeB) == (edgeB, edgeC)
}
}
Let's test it
let triangle = Triangle(edgeA: 5, edgeB: 5, edgeC: 5)
triangle.isEquilateral // true
or
let triangle = Triangle(edgeA: 2, edgeB: 2, edgeC: 1)
triangle.isEquilateral // false
A read-only property is a property with getter but no setter. It is always used to return a value.
class ClassA {
var one: Int {
return 1
}
var two: Int {
get { return 2 }
}
private(set) var three:Int = 3
init() {
one = 1//Cannot assign to property: 'one' is a get-only property
two = 2//Cannot assign to property: 'two' is a get-only property
three = 3//allowed to write
print(one)//allowed to read
print(two)//allowed to read
print(three)//allowed to read
}
}
class ClassB {
init() {
var a = ClassA()
a.one = 1//Cannot assign to property: 'one' is a get-only property
a.two = 2//Cannot assign to property: 'two' is a get-only property
a.three = 3//Cannot assign to property: 'three' setter is inaccessible
print(a.one)//allowed to read
print(a.two)//allowed to read
print(a.three)//allowed to read
}
}

Is it possible to set a variable via its own setter method? [duplicate]

With this simple class I am getting the compiler warning
Attempting to modify/access x within its own setter/getter
and when I use it like this:
var p: point = Point()
p.x = 12
I get an EXC_BAD_ACCESS. How can I do this without explicit backing ivars?
class Point {
var x: Int {
set {
x = newValue * 2 //Error
}
get {
return x / 2 //Error
}
}
// ...
}
Setters and Getters apply to computed properties; such properties do not have storage in the instance - the value from the getter is meant to be computed from other instance properties. In your case, there is no x to be assigned.
Explicitly: "How can I do this without explicit backing ivars". You can't - you'll need something to backup the computed property. Try this:
class Point {
private var _x: Int = 0 // _x -> backingX
var x: Int {
set { _x = 2 * newValue }
get { return _x / 2 }
}
}
Specifically, in the Swift REPL:
15> var pt = Point()
pt: Point = {
_x = 0
}
16> pt.x = 10
17> pt
$R3: Point = {
_x = 20
}
18> pt.x
$R4: Int = 10
You can customize the set value using property observer. To do this use 'didSet' instead of 'set'.
class Point {
var x: Int {
didSet {
x = x * 2
}
}
...
As for getter ...
class Point {
var doubleX: Int {
get {
return x / 2
}
}
...
Setters/getters in Swift are quite different than ObjC. The property becomes a computed property which means it does not have a backing variable such as _x as it would in ObjC.
In the solution code below you can see the xTimesTwo does not store anything, but simply computes the result from x.
See Official docs on computed properties.
The functionality you want might also be Property Observers.
What you need is:
var x: Int
var xTimesTwo: Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
You can modify other properties within the setter/getters, which is what they are meant for.
To elaborate on GoZoner's answer:
Your real issue here is that you are recursively calling your getter.
var x:Int
{
set
{
x = newValue * 2 // This isn't a problem
}
get {
return x / 2 // Here is your real issue, you are recursively calling
// your x property's getter
}
}
Like the code comment suggests above, you are infinitely calling the x property's getter, which will continue to execute until you get a EXC_BAD_ACCESS code (you can see the spinner in the bottom right corner of your Xcode's playground environment).
Consider the example from the Swift documentation:
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
Notice how the center computed property never modifies or returns itself in the variable's declaration.
In order to override setter and getter for swift variables use the below given code
var temX : Int?
var x: Int?{
set(newX){
temX = newX
}
get{
return temX
}
}
We need to keep the value of variable in a temporary variable, since trying to access the same variable whose getter/setter is being overridden will result in infinite loops.
We can invoke the setter simply like this
x = 10
Getter will be invoked on firing below given line of code
var newVar = x
Update: Swift 4
In the below class setter and getter is applied to variable sideLength
class Triangle: {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) { //initializer method
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get { // getter
return 3.0 * sideLength
}
set(newValue) { //setter
sideLength = newValue / 4.0
}
}
Creating object
var triangle = Triangle(sideLength: 3.9, name: "a triangle")
Getter
print(triangle.perimeter) // invoking getter
Setter
triangle.perimeter = 9.9 // invoking setter
You are recursively defining x with x. As if someone asks you how old are you? And you answer "I am twice my age". Which is meaningless.
You must say I am twice John's age or any other variable but yourself.
computed variables are always dependent on another variable.
The rule of the thumb is never access the property itself from within the getter ie get. Because that would trigger another get which would trigger another . . . Don't even print it. Because printing also requires to 'get' the value before it can print it!
struct Person{
var name: String{
get{
print(name) // DON'T do this!!!!
return "as"
}
set{
}
}
}
let p1 = Person()
As that would give the following warning:
Attempting to access 'name' from within it's own getter.
The error looks vague like this:
As an alternative you might want to use didSet. With didSet you'll get a hold to the value that is was set before and just got set to. For more see this answer.
Try using this:
var x:Int!
var xTimesTwo:Int {
get {
return x * 2
}
set {
x = newValue / 2
}
}
This is basically Jack Wu's answer, but the difference is that in Jack Wu's answer his x variable is var x: Int, in mine, my x variable is like this: var x: Int!, so all I did was make it an optional type.
Setters and getters in Swift apply to computed properties/variables. These properties/variables are not actually stored in memory, but rather computed based on the value of stored properties/variables.
See Apple's Swift documentation on the subject: Swift Variable Declarations.
Here is a theoretical answer. That can be found here
A { get set } property cannot be a constant stored property. It should be a computed property and both get and set should be implemented.
Update for Swift 5.1
As of Swift 5.1 you can now get your variable without using get keyword. For example:
var helloWorld: String {
"Hello World"
}
I don't know if it is good practice but you can do something like this:
class test_ancestor {
var prop: Int = 0
}
class test: test_ancestor {
override var prop: Int {
get {
return super.prop // reaching ancestor prop
}
set {
super.prop = newValue * 2
}
}
}
var test_instance = test()
test_instance.prop = 10
print(test_instance.prop) // 20
Read more

How to prevent duplicate code when initializing variables?

A class has some variables to be initialized in init() and at the same time, the class provides a function to restore these variables to their initial values in restoreInitValues(). Is there any way I can achieve this without setting these values twice (duplicate code) inside both init() and restoreInitValues()?
class Foo {
var varA: Int
var varB: Int
var varC: Int
init() {
//restoreInitValues() // error: call method before all stored proproties are initalized
//or I have to have duplicate code here as restoreInitValues below
varA = 10
varB = 20
varC = 30
}
func restoreInitValues() {
varA = 10
varB = 20
varC = 30
}
}
Personally I would assign the 3 default values to 3 class scope constants, then use those values to init and restore. You could also eliminate the assigning statements in the init if you want, and assign the value when you declare the var. In addition, by having your defaults defined in a class constant if you need to add any other functions to the class they'll be available for use.
class Foo {
let defaultA = 10
let defaultB = 20
let defaultC = 20
var varA: Int
var varB: Int
var varC: Int
init() {
varA = defaultA
varB = defaultB
varC = defaultC
}
func restoreInitValues() {
varA = defaultA
varB = defaultB
varC = defaultC
}
}
You could also define a struct, use it to assign your values, and then use your reset function to init.
struct values{
static let defaultA = 10
static let defaultB = 20
static let defaultC = 30
}
class test {
var a: Int = 0
var b: Int = 0
var c: Int = 0
init(){
resetValues()
}
func resetValues(){
(a, b, c) = (values.defaultA, values.defaultB, values.defaultC)
}
}
Use implicitly unwrapped optionals.
class Foo {
var varA: Int!
var varB: Int!
var varC: Int!
init() {
restoreInitValues()
}
func restoreInitValues() {
varA = 10
varB = 20
varC = 30
}
}
Update: Code's answer below about Implicitly Unwrapped Optionals is the answer. I'm not sure why I couldn't find it in the docs before. I'm leaving my answer for posterity, but you should accept Code's answer.
being new to swift, I gave it a try and found these two solutions, but I'm not sure if they're the best solutions.
The problem seems to be that swift tries hard to ensure that no class instance will ever have uninitialized properties (unless they're marked as optional) after initialization has been performed. It won't let you call non-static methods because you might use the instance before all properties are set. Also, it doesn't trust you to call another method which initializes all of the properties for you presumably because that would be really hard to verify.
For classes, use a private static method to return default values:
class Foo {
//...
init() {
(varA, varB, varC) = Foo.defaultValues()
}
func restoreInitValues() {
(varA, varB, varC) = Foo.defaultValues()
}
static private func defaultValues() -> ( Int, Int, Int ) {
return (10, 20, 30)
}
}
If you don't need a class, structs are copyable by value:
struct Foo {
//...
mutating func restoreInitValues() {
self = Foo()
}
}
or you could give up on restoreInitValues() and just do this:
var f = Foo()
f.varA = 10000
// instead of resetting the internal state of `f`, just replace it with
// a new `Foo` instance
f = Foo()
or you could use a static private method that modifies Foo instances, but to get around the compiler you have to make your properties be optional. This solution has a definite Ick factor:
class Foo {
var varA: Int?
var varB: Int?
var varC: Int?
init() {
Foo.resetValues(in: self)
}
func restoreInitValues() {
Foo.resetValues(in: self)
}
static private func resetValues(in foo: Foo) {
foo.varA = 10
foo.varB = 20
foo.varC = 30
}
}
This leads us back to the heart of the problem: swift requires that all properties be optional or initialized. Another solution would be to simply give all of the properties values (meaningless or not). The downside is that the property definitions may be misleading to someone reading the code for the first time.
class Foo {
var varA = -1
var varB = -1
var varC = -1
init() {
restoreInitValues()
}
func restoreInitValues() {
varA = 10
varB = 20
varC = 30
}
}
Last, check out the answers to this similar question: How to implement two inits with same content without code duplication

Property getters and setters

With this simple class I am getting the compiler warning
Attempting to modify/access x within its own setter/getter
and when I use it like this:
var p: point = Point()
p.x = 12
I get an EXC_BAD_ACCESS. How can I do this without explicit backing ivars?
class Point {
var x: Int {
set {
x = newValue * 2 //Error
}
get {
return x / 2 //Error
}
}
// ...
}
Setters and Getters apply to computed properties; such properties do not have storage in the instance - the value from the getter is meant to be computed from other instance properties. In your case, there is no x to be assigned.
Explicitly: "How can I do this without explicit backing ivars". You can't - you'll need something to backup the computed property. Try this:
class Point {
private var _x: Int = 0 // _x -> backingX
var x: Int {
set { _x = 2 * newValue }
get { return _x / 2 }
}
}
Specifically, in the Swift REPL:
15> var pt = Point()
pt: Point = {
_x = 0
}
16> pt.x = 10
17> pt
$R3: Point = {
_x = 20
}
18> pt.x
$R4: Int = 10
You can customize the set value using property observer. To do this use 'didSet' instead of 'set'.
class Point {
var x: Int {
didSet {
x = x * 2
}
}
...
As for getter ...
class Point {
var doubleX: Int {
get {
return x / 2
}
}
...
Setters/getters in Swift are quite different than ObjC. The property becomes a computed property which means it does not have a backing variable such as _x as it would in ObjC.
In the solution code below you can see the xTimesTwo does not store anything, but simply computes the result from x.
See Official docs on computed properties.
The functionality you want might also be Property Observers.
What you need is:
var x: Int
var xTimesTwo: Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
You can modify other properties within the setter/getters, which is what they are meant for.
To elaborate on GoZoner's answer:
Your real issue here is that you are recursively calling your getter.
var x:Int
{
set
{
x = newValue * 2 // This isn't a problem
}
get {
return x / 2 // Here is your real issue, you are recursively calling
// your x property's getter
}
}
Like the code comment suggests above, you are infinitely calling the x property's getter, which will continue to execute until you get a EXC_BAD_ACCESS code (you can see the spinner in the bottom right corner of your Xcode's playground environment).
Consider the example from the Swift documentation:
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
Notice how the center computed property never modifies or returns itself in the variable's declaration.
In order to override setter and getter for swift variables use the below given code
var temX : Int?
var x: Int?{
set(newX){
temX = newX
}
get{
return temX
}
}
We need to keep the value of variable in a temporary variable, since trying to access the same variable whose getter/setter is being overridden will result in infinite loops.
We can invoke the setter simply like this
x = 10
Getter will be invoked on firing below given line of code
var newVar = x
Update: Swift 4
In the below class setter and getter is applied to variable sideLength
class Triangle: {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) { //initializer method
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get { // getter
return 3.0 * sideLength
}
set(newValue) { //setter
sideLength = newValue / 4.0
}
}
Creating object
var triangle = Triangle(sideLength: 3.9, name: "a triangle")
Getter
print(triangle.perimeter) // invoking getter
Setter
triangle.perimeter = 9.9 // invoking setter
You are recursively defining x with x. As if someone asks you how old are you? And you answer "I am twice my age". Which is meaningless.
You must say I am twice John's age or any other variable but yourself.
computed variables are always dependent on another variable.
The rule of the thumb is never access the property itself from within the getter ie get. Because that would trigger another get which would trigger another . . . Don't even print it. Because printing also requires to 'get' the value before it can print it!
struct Person{
var name: String{
get{
print(name) // DON'T do this!!!!
return "as"
}
set{
}
}
}
let p1 = Person()
As that would give the following warning:
Attempting to access 'name' from within it's own getter.
The error looks vague like this:
As an alternative you might want to use didSet. With didSet you'll get a hold to the value that is was set before and just got set to. For more see this answer.
Try using this:
var x:Int!
var xTimesTwo:Int {
get {
return x * 2
}
set {
x = newValue / 2
}
}
This is basically Jack Wu's answer, but the difference is that in Jack Wu's answer his x variable is var x: Int, in mine, my x variable is like this: var x: Int!, so all I did was make it an optional type.
Setters and getters in Swift apply to computed properties/variables. These properties/variables are not actually stored in memory, but rather computed based on the value of stored properties/variables.
See Apple's Swift documentation on the subject: Swift Variable Declarations.
Here is a theoretical answer. That can be found here
A { get set } property cannot be a constant stored property. It should be a computed property and both get and set should be implemented.
Update for Swift 5.1
As of Swift 5.1 you can now get your variable without using get keyword. For example:
var helloWorld: String {
"Hello World"
}
I don't know if it is good practice but you can do something like this:
class test_ancestor {
var prop: Int = 0
}
class test: test_ancestor {
override var prop: Int {
get {
return super.prop // reaching ancestor prop
}
set {
super.prop = newValue * 2
}
}
}
var test_instance = test()
test_instance.prop = 10
print(test_instance.prop) // 20
Read more