So, I have this case where var3's value depends on both var1 and var2. Basically, this can be translated into two forms:
A) Using computed property for var3
class bla {
var var1: Int = 0
var var2: Int = 0
var var3: Int {
return var1 * var2
}
}
B) Using stored property with property observers for both var1 and var2
class bla {
var var1: Int = 0 {
didSet {
var3 = var1 * var2
}
}
var var2: Int = 0 {
didSet {
var3 = var1 * var2
}
}
var var3: Int = 0
}
However, in my case where I have to handle this, the variables are all parts of a huge model object that is being used inside a UITableViewCell. Being used in such context, it really needs to be as efficient as possible, which is the reason I'm trying to stay away from using computed properties for such case.. cause as I understand, when Swift get var3's value under A class implementation, it will compute its value on-the-fly, rather than get it cached like the way with B class implementation. Right? (Does Swift cache computed property's values in any way?)
Now I am really wondering what the best approach one should go with. In fact, the whole point I migrated some of those variables into the model object rather than computing them on-the-fly inside the cell, is to get the load off from the cell and get as much smooth scrolling as possible, which tells that using computed property makes my efforts meaningless. On the other hand, I came to cases where one of those variables depends on 3~4 other variables, which makes it necessary to re-compute it when each of those values get changed, which is also not efficient, I guess.
Any advices? Am I doing it wrong?
Here’s the approach I currently use where an instance property needs to be computed at some point and cached for subsequent accesses:
class AClassWithCachedInstanceVariables {
private var _var1: Var1Type?
var var1: Var1Type {
if let var1 = self._var1 {
// cache hit
return var1
} else {
// cache miss
let var1 = self.getVar1()
self._var1 = var1
return var1
}
}
private var _var2: Var2Type?
var var2: Var2Type {
if let var2 = self._var2 {
// cache hit
return var2
} else {
// cache miss
let var2 = self.getVar2()
self._var2 = var2
return var2
}
}
/* Extra: let’s say we also want to require var1 and var2
to be recomputed whenever this other variable is changed */
var otherVar: OtherVarType? {
didSet(oldOtherVar) {
if otherVar != oldOtherVar {
self._var1 = nil
self._var2 = nil
}
}
}
private func getVar1() -> Var1Type {
// compute stuff...
return Var1Type()
}
private func getVar2() -> Var2Type {
// compute stuff...
return Var2Type()
}
}
Related
I've been stuck trying to fix an issue in my application that prevents me from getting my desired result.
When pushing segue to the next view controller, I passed two objects of the same class to var original:[Person] = [] and var edited:[Person] = []
if (segue.identifier == "showSummary") {
let vc = segue.destination as! SummaryViewController
vc.original = myObject
vc.edited = Bill.calculateBill(myObject, switchServiceCharge.isOn, switchGST.isOn)
vc.gst = (switchServiceCharge.isOn, switchGST.isOn)
}
Before calculateBill, the original value of a property inside is 11.
The calculateBill is a class function in another class Bill:
class func calculateBill(_ bill: [Person],_ svcCharge: Bool,_ GST: Bool) -> [Person] {
var mutableBill = bill
for i in 0 ..< mutableBill.count {
for j in 0 ..< mutableBill[i].items.count {
let tax = Bill.getGSTForIndividual(mutableBill[i].items[j].itemPrice, svcCharge, GST)
mutableBill[i].items[j].itemPrice += tax
}
}
return mutableBill
}
class func getGSTForIndividual(_ individualAmt: Decimal,_ svcCharge: Bool,_ GST: Bool) -> Decimal {
var taxCost : Decimal = 0.00
let SERVICE_CHARGE = Bill.getServiceCharge() //0.10
let GOODS_SERVICE_TAX = Bill.getGST() //0.07
if (svcCharge && GST) {
taxCost = individualAmt * SERVICE_CHARGE
taxCost = taxCost + ((individualAmt + taxCost) * GOODS_SERVICE_TAX)
}
else if (!svcCharge && GST) {
taxCost = individualAmt * GOODS_SERVICE_TAX
}
else if (svcCharge && !GST) {
taxCost = individualAmt * SERVICE_CHARGE
}
else {
taxCost = 0.00
}
return taxCost
}
When I did a print() to test whether the properties inside are different, they both yield the same results somehow...
print(original[0].items[0].itemPrice) //12.947000000000000123904, originally 11
print(edited[0].items[0].itemPrice) //12.947000000000000123904
What exactly is going on and why do both the objects have the same properties even though I have declared them differently?
I'm guessing that Person is probably declared as a class, not a struct. This means that it's a reference type, and when you:
var mutableBill = bill
you're not actually making a copy of bill, but rather another reference to the same object. So, when you change the properties on mutableBill, you also change the properties on bill, since they both point to the same object.
The solution is to make an actual copy of bill. The two options are either to declare Person as a struct, in which case you will always get a copy, or else to make an initializer on Person that takes another Person and initializes itself using the passed-in Person's properties:
init(person: Person) { ... }
I've got a question on property observers. There's some example code below. What I want is for the property Analysis.hasChanged to be updated to true if a.value is changed. Is there a way I can do this?
class Number {
var value: Double
init(numberValue: Double) {
self.value = NumberValue
}
}
class Analysis {
var a: Number
var hasChanged = false
init(inputNumber: Number) {
self.a = inputNumber
}
}
testNumber = Number(numberValue: 4)
testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged) // will still print "false", but I want it to print "true"
In the end, I want the user to be able to be notified if any of their analyses use numbers that have been changed so that they can update the results of the analyses if they choose.
You can use the built-in property observers provided by Swift.
Every time you set a new value, the didSet will be called. You just need to attach the closure, wrapping the desired behaviour, to the Number class
class Number {
var valueDidChangeClosure: (()->())?
var value: Double {
didSet {
//won't call the valueDidChangeClosure
//if the value was changed from 10 to 10 for example..
if oldValue != value {
valueDidChangeClosure?()
}
}
}
init(numberValue: Double) {
self.value = numberValue
}
}
class Analysis {
var a: Number
var hasChanged = false
init(inputNumber: Number) {
self.a = inputNumber
self.a.valueDidChangeClosure = {
self.hasChanged = true
}
}
}
let testNumber = Number(numberValue: 4)
let testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged) // will print "true"
I would do something like this, I apologize in advance if I have some syntax wrong (I usually use C/C++, think of this as more psudo code since you'd have to have a way to copy Number classes, etc.).
class Number {
var value: Double
init(numberValue: Double) {
self.value = NumberValue
}
}
class Analysis {
var a: Number
var _a: Number
bool hasChanged() {
if (a != _a) {
_a = a
return true;
}
return false;
}
init(inputNumber: Number) {
self.a = inputNumber
self._a = self.a
}
}
testNumber = Number(numberValue: 4)
testAnalysis = Analysis(inputNumber: testNumber)
print(testAnalysis.hasChanged()) // will print "false"
testNumber.value = 10
print(testAnalysis.hasChanged()) // will still print "false", but I want it to print "true"
In the end, I want the user to be able to be notified if any of their analyses use numbers that have been changed so that they can update the results of the analyses if they choose.
I don't know if this really addresses that question, I based my answer off of the code you provided. So there may be additional functionality if you want there to be some triggering method (instead of calling .hasChanged()).
Comparing doubles (and any other floating point type) with '=' or '!=' is not a good idea.
Use epsilon function instead.
Details: jessesquires.com/blog/floating-point-swift-ulp-and-epsilon/
I'm trying to fill a UIPickerView with options using a static variable. Is there a more swifty way in creating a list of metric height instead of a for-loop?
Here's what I got:
static var heightMetric: [(key: String, value: String)] {
var items: (key: String, value: String) = []
for item in 30...330 {
items.append(("\(item)", "\(item) cm"))
}
return items
}
For the imperial form, any idea on a good way to create the list of options in a format as 5' 8" to fill a UIPickerView?
No need for the for-loop actually:
static var heightMetric: [(key: String, value: String)] = {
return (30...330).map {
("\($0)", "\($0) cm")
}
}()
You can even drop the explicit type and simply write static var heightMetric = { ...
Regarding the foot and inch form I do not know of any built-in functionality for that. You either have to calculate the value based on the centimeter value by doing a little bit of math or create a map similar to the above one where you might make use of the % operator to split the number into feet and inches. For example:
static var heightMetricImperial = {
return (12...120).map {
("\($0)", "\($0 / 12)' \($0 % 12)\"")
}
}()
Complete example:
class K {
static var heightMetric = {
(30...330).map {
("\($0)", "\($0) cm")
}
}()
static var heightMetricImperial = {
(12...120).map {
("\($0)", "\($0 / 12)' \($0 % 12)\"")
}
}()
}
print(K.heightMetric)
print(K.heightMetricImperial)
I have been trying for the last 18 months taking all my code out of the ViewController to make it more like MVC should. ( Hence the title )
I've made a small step on my own by being able to do the following within one Class object, but now I want to break it down further
A very generic example: one file contains the data
struct Data {
var x = 0
var y = 0
}
and one file for the operation
class Adder {
var myObject = MyClass()
var z = 1
func addThem() {
z = myObject.x + myObject.y
}
}
now for the salient parts of the ViewController:
var data = Data()
var adder = Adder()
#IBAction func buttonPressed(sender: UIButton) {
// user input via textfield
data.x = Int(numeralOne.text!)!
data.y = Int(numeralTwo.text!)!
adder.addThem() // *
answerLabel.text = String(adder.z)
}
Ultimately I'd like to omit the line commented with the asterisk. I thought OO's encapsulation of (data) away from (adder) allows for adder.z to just automatically update in the background without involving the ViewController. That way a subtractor class (say) can operate on the same two struct properties.
My question? How can correctly referencing them from the VC.
PS. if i include return statements in the function it makes no difference.
It's not clear from your code exactly what the relationship is between the Data struct and MyClass but if a MyClass object has the values of x & y that you need then the following will work
class MyClass {
var x = 1
var y = 2
}
class Adder {
var myObject = MyClass()
var z: Int {
return myObject.x + myObject.y
}
}
let adder = Adder()
print(adder.z)
I am getting the error:
Immutable value of type 'Array Character>' only has mutating members of name removeAtIndex()
The array should have contents because that removeAtIndex line is in a loop who's condition is if the count > 1
func evaluatePostFix(expression:Array<Character>) -> Character
{
var stack:Array<Character> = []
var count = -1 // Start at -1 to make up for 0 indexing
if expression.count == 0 {
return "X"
}
while expression.count > 1 {
if expression.count == 1 {
let answer = expression[0]
return answer
}
var expressionTokenAsString:String = String(expression[0])
if let number = expressionTokenAsString.toInt() {
stack.append(expression[0])
expression.removeAtIndex(0)
count++
} else { // Capture token, remove lefthand and righthand, solve, push result
var token = expression(count + 1)
var rightHand = stack(count)
var leftHand = stack(count - 1)
stack.removeAtIndex(count)
stack.removeAtIndex(count - 1)
stack.append(evaluateSubExpression(leftHand, rightHand, token))
}
}
}
Anyone have any idea as to why this is? Thanks!
Because all function parameters are implicitly passed by value as "let", and hence are constant within the function, no matter what they were outside the function.
To modify the value within the function (which won't affect the value on return), you can explicitly use var:
func evaluatePostFix(var expression:Array<Character>) -> Character {
...
}