How to build an array in swift re-using a variable - ios

I plan to use a class object variable to capture user entered data sets in recurring cycles and when a data set is complete add this to an array holding all these class variables.
Simple example code in playground:
class Example {
var a = 1
var b = 5
}
var exArray = [Example] () // this is the array holding the class variables
var anExample = Example () // default-init the "re-usable" variable
exArray.append(anExample)
exArray[0].a // results in 1 as expected
exArray[0].b // results in 5 as expected
exArray.count // 1 as expected
// now changing the re-usable variable properties with new user entered data
anExample.a = 3
anExample.b = 7
// appending the altered variable
exArray.append(anExample)
exArray[0].a // shows 3 where I still expect 1
exArray[0].b // shows 7 where I expect 7
exArray.count // shows 2 as expected
it seems the array holds the variable itself (pointer?) not a copy of the variable, so this keeps changing within the array. Any ideas how I can "reset" the class variable without changing the array member?

If you create an instance of a class and assign to a variable, the variable contains a reference to the instance and not the instance itself.
When you add the variable to the array, you add the reference to the class instance. If you later change the instance properties and add to the array, you add the same reference as you added before. Result: all of what you insert into the array is a variable containing a reference to the same class instance.
Depending on your app architecture and logic, you might want to turn the class into a struct - differently from a class, which is a reference type, the struct is a value type, so when moved around, a copy of it is created. That happens when:
you assign the variable to another variable
you pass it to a function or method (unless the inout modifier is used)
As a direct consequence, if you insert a struct instance into an array, you call a method, so a copy of the struct is created and inserted into the array - that copy has no relationship with the original struct, besides having the same values at the moment it is copied. So any change you do to any of them, it is not reflected to the other.

If you want your data copied when you assign it, you can define Example as a struct instead of a class See this answer for the difference between struct and class
struct Example {
var a = 1
var b = 5
}
Now when you assign it to the array, it will add a copy to the array with the data at the point where you assigned it.
But if you want to keep using a class you should create a new instance with the data you want before you append it to the array.

Related

How to create a empty variable that can take the value type of a class

Hey I'm trying to figure out how to either set a variable to the type of an empty class. Like this if it weren't an error:
Code:
var PlayerEquipped = class() // failed attempt at trying set up a blank variable that can take the type of a class
Or make a variable that i can change in the future. So basically i can create a global variable like this with a class assigned to it with no problems.
Code:
var PlayerEquipped = House()
//In another .swift file i have the 2 classes
class House {
init() {
}
}
class House2 {
init() {
}
}
But even though its setup with "var" i still get an error when i try to change that "SelectClass" variable to a different class. For example If i were to make a string variable with text "Hello" in-side then later down in my view did load decide to change that variable text to "GoddBye" it would let me do that. But if i try to change the "SelectedClass" Variable to a different class I get this error. It saying 'cannot assign value of type saintsRB to type saintsLB'
Code:
var PlayerEquipped = House()
//down in view didload:
PlayerEquipped = House2() // Error here
try using
var PlayerEquipped: AnyObject
or its Optional equivalent
var PlayerEquipped: AnyObject?
You have a couple of options depending on what you want to achieve:
Make a protocol (e.g. Buildable). Have all your houses implement it. Declare your variable as Buildable (e.g. var house:Buildable? - note the ?, so you don't have to init it, it could be nil) - This is usually how I would do it, trying to avoid inheritance
Make a class (e.g House). Have all your houses (House1, House2, etc) inherit form House. Declare your variable as House (e.g. var house:House?)
Declaring as Any or AnyObject might be valid, but it limits you heavily and it's probably not what you want to do in this situation.
And as an advice, try to grasp these basic principles before going forward and code.

Understanding to visualize swift initializers

I am unable to visualise the working of initializers in Swift. For this example from the official documentation.
struct Fahrenheit{
var temp : Double
init(){
temp = 32.0
}
}
var f = Fahrenheit()
print(" \(f.temp)")
//Prints 32.0
Here's what I understood till now, PLEASE correct me when i am wrong:
struct is a value type.
variable temp is a stored property that stores values inside the memory space where the struct is defined (in memory).
when we create a variable 'f' is an instance(object) copy of the Structure Fahrenheit is made in another memory space having the same properties.
What i am unable to understand is that what is
init(){
temp = 32.0
}
doing to the instance f.
When do we use intializers in general. (Main purpose : using an example).
Also the difference between functions, closures and initializers, how they are stored in memory?
It is definitely important to deeply understand the process of creation of object (as an instance of a class or an instance of the struct). Objects are created based on a template defined in class or struct, in a "space" I like to name as "Object space". So, the object is the instance of struct Fahrenheit in an "Object space" and you could try to see it (visualize) as a balloon. The variable f is a reference to this object and it is been used as a tool to access this balloon (object, instance).
I suggest you to take a look to Apple's documentation:
https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
Here you can see this:
And - In my opinion, it is a good way how to visualize objects and references to objects.
So, when the system executes: var f = Fahrenheit(), first - it makes an balloon in Object space, it invokes initialiser (implicit or explicit) to set initial values, than it makes an reference (f) - that points to the just-borned-object.
So:
init(){
temp = 32.0
}
does not make an effect to - f, it makes an effect inside of object (balloon), and f is been uses to access to the balloon. (If there is no reference, the ARC will kill the object)

Should I use var or let for an object later mutated?

I have an iOS app that, upon startup, loads objects from persistent storage that will be manipulated later in the app. For example, on startup it loads patient profiles in an array. Does it matter if I define the items I add to the array as variables, versus constants, if they will be modified by the app later (say in a different View Controller)?
In my App Delegate, I load them like this:
func loadProfiles() {
var profileRecord: COpaquePointer = nil
if sqlite3_prepare_v2(db, "SELECT profilesid, objectSyncStatus, profileName, profileRelationship, profileFName, profileLName, profileAddress, profileCity, profileState, profileZip FROM profiles", -1, &profileRecord, nil) == SQLITE_OK {
if sqlite3_step(profileRecord) == SQLITE_ROW {
// Load profile stubs for each person
var newProfile = DBProfile(withDatabase: db, fromRecord: profileRecord, withLanguage: appLanguage, loadAllData: false)
patientProfiles.append(newProfile)
}
}
}
Of course, I get a warning that newProfile is not mutated, and it wants to change it to let newProfile = ... before it is added to the array. But, if I do that, will it become immutable later?
Thanks for the answers.
The compiler is actually really good at determining whether you should use let or var, and in this case, it is correct.
var should be used anywhere the data will be mutated. For example:
A struct value where the properties will be mutated
Pointers (like your COpaquePointer)
Instances of classes that will be reassigned to different class instances
let should be used anywhere the data will not be mutated. For example:
Constants
Values to be added to arrays, dictionaries, arguments to functions, etc.
Class instances where the instance will not be reassigned.
Note that for instances of classes, you can still modify the properties of the class even if it is defined as let. var should only be used in this case when the class itself will be reassigned.
In the case of your newProfile variable, during it's lifetime it is never mutated. The object is created, then immediately appended to your array. That array needs to be defined with var because it is mutated with that append, but newProfile never gets changed. You can change the value that was appended from newProfile through the array at a later date if you'd like because the patientProfiles array is mutable.
A good practice for when you are not sure whether to use let or var is to start with let and see if the compiler complains. If it does, then change it to var.
I see that you do not quite understand what is constant and how it works with value and reference types.
You can think of constant as glass box with lock and key.
Once you put something in box and lock it you threw away the key so you can see box contents (read properties and call non-mutating methods) but can not change it.
Words mutated and immutable can be only applied to value types because in case of value type the box holds value itself and if some method of value can change value then it must be marked with keyword mutating so it will not be visible through box glass.
In case of reference type the box holds reference to instance of type. If you define constant of reference type then you have box with reference. You can not change the reference, but you can read it and then go and find instance by that reference and do whatever you like with that instance.
In your case you define constant:
let newProfile = DBProfile(...)
and DBProfile is class (reference type).
You can not assign another reference to newProfile but you do whatever you like with object that referenced by newProfile. So you append it to patientProfiles array and you can get it later from this array and do what you want.

Swift Array Pass by Value...same memory address?

Can someone please clear this up for me.
I understand (thought) Swift passes arrays by value as it is a struct.
But when I pass an array via segue to the next view controller it appears to me that it is passing by reference, as when I check the memory address of the array they are the same.
This is how I'm checking
println("\(unsafeAddressOf(runs))") // 0x0000000174240c00
I would have thought these memory addresses would be different ? Or am I just confusing myself.
The Run / StaffTask classes both inherit from NSObject for saving purposes.
class Run: NSObject, NSCoding { }
Furthermore, if I access a item in the array
var service = self.staffTasks[indexPath.row]
and edit the value, both the service variable value and the element in the array are updated. They have the same memory address, as shown by
println("\(unsafeAddressOf(service)) \(unsafeAddressOf(self.staffTasks[indexPath.row]))")
Also...
staffTasks are a subset of a larger array called runs
When I search for the service object, with the larger set, I find that they are also the same memory address
if let index = find(self.runs, self.staff) {
println("local \(unsafeAddressOf(self.staff)) main list \(unsafeAddressOf(self.runs[index]))")
}
I am using NSCoding to save my objects, so I thought I would need to find the service object within the larger set, replace that object and then save them out.
But turns out I don't need to, I am able to edit the service variable and array of runs is updated on automatically ... like a reference type would be.
Passing? .. setting the Var
in prepare for segue, just setting the local var in the second VC using the standard way, var runsInSecondVC = runs. Not using, &pointers or any other weirdness.
Thanks for any help.
Class Basic Details
class Run: NSObject, NSCoding {
var runDate:NSDate!
var staffName:String!
var staffEmail:String!
var runTasks:[StaffTask]!
}
class StaffTask: NSObject, NSCoding {
var taskName:String
var taskTime:NSInteger
var clientName:String!
}
These are the basic of these classes. Nothing complicated.
passes arrays by value
No. Swift arrays are a value type. This does not mean they are pass-by-value. It means that you can mutate an array by way of a reference without affecting other references to which that array has been assigned.
You are getting a little confused about the array itself and the contents of the array.
..if I access a item in the array
var service = self.staffTasks[indexPath.row]
and edit the value, both the service variable value and the element in the array are updated. They have the same memory address...
Putting an object into an array doesn't copy the object. In fact, the array holds a reference to the object, so the line above actually says "make a variable, service that holds a reference to the object that self.staffTasks[indexPath.row] holds a reference to". There is only one object, now referred to in two places, so when you update the object that change is visible through both variables.

Simple Clarification Objects Swift Language

I have a very simple question on something that I may have misunderstood.
I have two UIViews "A" and "B". If I write :
let A = UIView() // Or something else
let B = A
and then I change properties of B (for exemple the frame), will the properties of A change too ?
I though not, but I was animating a view, so I had the initial view and the final view. I created a transition view like this :
let transitionView = finalView
and then I changed the properties of transitionView, the position of a label for exemple.
When I added the final view at the end of the animation, the label was at the new position.
Why ? Thanks
In swift types are split in 2 main categories:
reference types
value types
Classes are reference types; structs (which include arrays and dictionaries), basic data types (int, float, string, etc.), and enums are all value types.
A value type is always passed by value, which means when assigning to a variable or passing to a function/method, a copy of the original data is created. There's an exception to this rule: a function/method can use the inout modifier on a value type parameter to have it passed by reference.
Note that the compiler and the runtime usually do optimizations, so a copy is not always created unless strictly needed - what's important is that we, as developer, know that we are working on a copy and not on the original data.
A reference type is always passed by reference, which means when assigning it to a variable or passing it to a function/method, a reference to the data and not the data itself is assigned/passed.
UIView is a class, so when you create an instance, assign it to a variable, then assign that variable to another variable, the reference to the instance and not the instance itself is assigned. Both variables point to the same UIView instance. Any change made to the instance is visible to all variables referencing that instance.
Suggested reading: Classes and Structures
Because B and A are not two views. They are references to the same UIView object.
That, in turn, is because class instances are passed as reference types in Swift.
See now my little essay on this topic here: https://stackoverflow.com/a/27366050/341994

Resources