Swift: Is Correct to Use Stored Properties as Computed Properties

swift: Is correct to use stored properties as computed properties

Not sure why, but lazy variables combined with computed properties result in an error:

'lazy' attribute may not be used on a computed property

But this seems to work:

class MyClass {
@lazy var customView: NSView = {
let view = NSView()
// customize view
return view
}()
}

Advantage of computed properties (gettable ones only) vs. stored properties

Computed Properties

var sayGoodMorningToUserComputed: String {
return greeting + username
}

sayGoodMorningToUserComputed acts just like a function. If a change has been made to greeting or username, then sayGoodMorningToUserComputed will return an up-to-date result that will be the concatenation of the current values.

You would want to use this if you want to ensure your returned value is computed off the latest values of its dependencies (greeting and username).

In the case that both dependencies are final, then it's very likely that the compiler would optimise this computed property into a stored property, because it knows the dependencies can't change

Stored properties

var sayGoodMorningToUserStored = greeting + username

sayGoodMorningToUserStored is just a variable, with nothing special going on. However, it's only set once, whenever the containing scope is initialized. It's computed once, stored and remains constant until it is overwritten by an external source. As such, if greeting or username changes, there will be no effect on sayGoodMorningToUserStored, because it's been computed from the old values, and stored.

You would want to use this if you want to improve performance by caching the result of a computation whose dependencies are constant.

What makes a property a computed property in Swift

First, this is about variables, not properties. Any variable can be a computed variable. A property is just one way to use a variable.

I think on the whole you are making a big mistake in putting a stored variable with setter observers side by side with a computed variable. They are unrelated!

Think of a computed variable as something that looks and acts like a variable when you use it — you get and (maybe) set it — but is in fact a function (or a pair of functions). It is just a compact way of calling a function. That's all it is.

A stored variable with observers, on the other hand, is just a stored variable that also has some observers.


Okay, on to your questions:

  1. I wonder what makes a property a computed property? Is is correct that as long as the property has a getter and return it is a computed property?

Yes. It's a computed variable because you declared it using the syntax that makes it a computed variable (with the curly braces).


  1. Are all my understandings (a, b & c) correct? If not would be nice of you to point out

Yes. I think your "c" is quite insightful: a computed variable does not need a setter observer because it has (gasp!) a setter!


  1. Why is it not allowed to initialize an computed property? (Please see the figure below) And when I do so the compiler gives out the warning Cannot call value of none-function type "int" What's the meaning of this error?

There is no sense in which a computed variable "has" a value — it is computed! it's just some functions! — so it makes no sense to assign it an "initial" value.

How properties( stored and computed) are accessed during instantiation?

This is one of the differences between stored properties and computed properties.

The stored properties scale and mouthCurvature are initialized when an instance of FaceView is instantiated.

In your first example skullRadius is a computed property which is computed when your FaceView instance has that property called upon, after instantiation.

In the second example skullRadius is a stored property which is initialized at instantiation. The other properties are not yet available at that point because they may or may not be initialized.

Here's how you can refer to another property during initialization:

class test {
let foo = 1 // stored property
let bar = 2 // stored property
let buzz: Int // stored property not initialized when other stored properties are initialized
init() {
buzz = foo + bar // initialized after other stored properties
}
}

Example showing how lazy properties can be modified:

class Test {
lazy var foo: Int = { return 5 }()
}

let test = Test()
print(test.foo) -> "5"
test.foo = 10
print(test.foo) -> "10"

Can stored property in Swift have getter and setter?

You have willSet and didSet.

That should be enough of an answer, but stackoverflow thinks it is too short :-)

Difference between computed property and property set with closure

In short, the first is a stored property that is initialized via a closure, with that closure being called only one time, when it is initialized. The second is a computed property whose get block is called every time you reference that property.


The stored property’s initialization closure is called once and only once, but you can later change the value of the stored property (unless you replace var with let). This is useful when you want to encapsulate the code to initialize a stored property in a single, concise block of code.

The computed property’s block, however, is called each time you reference the variable. It’s useful when you want the code to be called every time you reference the computed property. Generally you do this when the computed property needs to be recalculated every time you reference the stored property (e.g. recalculated from other, possibly private, stored properties).

In this case, you undoubtedly want the stored property (the first example), not the computed property (the second example). You presumably don't want a new push behavior object each time you reference the variable.


By the way, in your first example, you internally reference to it being instantiated lazily. If you want that behavior, you must use the lazy keyword:

lazy var pushBehavior: UIPushBehavior = {
let behavior = UIPushBehavior()
behavior.setAngle(50, magnitude: 50)
return behavior
}()

If, however, the property is static, it is automatically instantiated lazily.

What is the difference between the following 3 declarations?

1.

var title: UILabel {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}

It is a read only computed property. Computed properties cannot be let. These are calculated using other stored/computed properties. So they don't have any backing store of their own. Hence, computed properties are always declared as var.

2.

let title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()

It is a stored property. This is assigned a closure that returns a UILabel object. This closure is executed during the instantiation process of the object and the returned UILabel object is assigned to title.

3.

lazy var title: UILabel = {
let label = UILabel()
textLabel.font = .systemFontOfSize(13)
return label
}()

It is a lazy stored property. It is also assigned a closure that returns a UILabel object. But this closure is not executed during the instantiation process. It is executed whenever this property is first used. After the execution of the closure, the UILabel object returned is assigned to title.

How to have stored properties in Swift, the same way I had on Objective-C?

Associated objects API is a bit cumbersome to use. You can remove most of the boilerplate with a helper class.

public final class ObjectAssociation<T: AnyObject> {

private let policy: objc_AssociationPolicy

/// - Parameter policy: An association policy that will be used when linking objects.
public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {

self.policy = policy
}

/// Accesses associated object.
/// - Parameter index: An object whose associated object is to be accessed.
public subscript(index: AnyObject) -> T? {

get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
}
}

Provided that you can "add" a property to objective-c class in a more readable manner:

extension SomeType {

private static let association = ObjectAssociation<NSObject>()

var simulatedProperty: NSObject? {

get { return SomeType.association[self] }
set { SomeType.association[self] = newValue }
}
}

As for the solution:

extension CALayer {

private static let initialPathAssociation = ObjectAssociation<CGPath>()
private static let shapeLayerAssociation = ObjectAssociation<CAShapeLayer>()

var initialPath: CGPath! {
get { return CALayer.initialPathAssociation[self] }
set { CALayer.initialPathAssociation[self] = newValue }
}

var shapeLayer: CAShapeLayer? {
get { return CALayer.shapeLayerAssociation[self] }
set { CALayer.shapeLayerAssociation[self] = newValue }
}
}

Swift function vs lazy var vs computed property - difference?

  • lazy vars are actually stored properties, so you can't put it in extensions or anywhere stored properties are not allowed.
  • The getter for computed properties is run every time you refer to that property. This can be significant especially if the getter is time-consuming or has side-effects to other parts of the code.
  • The getter for lazy vars are only run when the property is first referred to and never again.
  • lazy vars are variables. You can mutate them.
  • Computed properties can optionally have a setter, so sometimes they are read-only.
  • Using a function like that is very similar to a read only computed property. You just have to add () when getting its value.


Related Topics



Leave a reply



Submit