Posted on August 14, 2015
One of the world's ongoing discussions about Swift is when to use structs when to use classes. I think I'm going to contribute some of my own ideas today.
Value VS Reference
The answer is simple: when you need the value semantics (the so-called value semantics is that an object is copied by the system standard replication, and the copied objects are unrelated to each other, you can independently change each other) when you use the struct, when you need to reference semantics (so-called value semantics is an object is copied by the system standard, The class is used when there is no relationship between the copied objects and can be changed independently of each other. That's it!
Welcome to come again next week ...
Wait a minute!
What's wrong?
That doesn't answer the question.
What do you mean? That's it!
Yes, but ...
But what?
What is value semantics and referential semantics?
Oh, this one. Maybe I should talk about this next.
And how do they relate to structs and classes?
Good.
All of the problems are due to where the data and data are stored. We typically present data in local variables, parameters, properties, and global variables. Fundamentally, there are two different ways to store data in all of these places.
In value semantics, the data exists directly in the location where it is stored. In reference semantics, the data exists elsewhere, and a reference to the data is stored in the stored location. This difference may not be so obvious when you get the data. And when you copy that storage area, this difference will show up. In the value semantics, you get a copy of the new data, and under the referential semantics you get a copy of the new reference to the same data.
This is really abstract. Let's take a look at an example and temporarily remove the swift problem from your mind, let's look at an OC example:
@interface Someclass:nsobject @ PropertyInt Number; @End@implementation SomeClass @Endstruct SomeStruct {int Number; }; SomeClass *Reference= [[SomeClass alloc] init];Reference. Number= the; SomeClass *reference2 =Reference;Reference. Number= +; NSLog (@"The number in Reference2 is%d", Reference2. Number); struct SomeStruct value = {}; Value. Number= the; struct SomeStruct value2 = value; Value. Number= +; NSLog (@"The number in value2 is%d", value2. Number);
Printing results:
The number in reference2 is 43The number in value2 is 42
Why is there such a difference?
Code SomeClass *reference = [[SomeClass alloc] init] Creates an instance of a new SomeClass type in memory and assigns a reference to that instance in the variable. Code REFERENCE2 = Reference assigns a reference to the same object in a new variable. Now all two variables point to the same object, while Reference.number = 43 modifies the value of the number property stored in that object. So when you print the value of the number property of an object, the result is 43.
code struct SomeStruct value = {} Creates an instance of SomeStruct and assigns a value to the variable. Code value2 = value copies a copy of this instance in the second variable. Each variable contains a separate piece of data. Code Value.number = 43 modifies only the data in the value variable, and then the result is still 42 when printing the number of value2.
This example corresponds to the following swift example:
Class SomeClass {varNumber:int =0}structsomestruct {varNumber:int =0}varReference = SomeClass () Reference.number = the varReference2 = Reference Reference.number = +Print"The number in Reference2 is \ (reference2.number)")var value= SomeStruct ()value. Number = the varvalue2 =value value. Number = +Print"The number in value2 is \ (value2.number)")
The same as the previous print results:
The number in reference2 is 43The number in value2 is 42
Experience with value types
Value types are not a new concept, but for many people they feel that this is new. Why is it?
Structs are not very common in most OC code. We usually contact them in the form of CGRect or cgpoint and other similar structures, but generally do not create our own structures. One reason is that they are not so practical. It is really difficult to use the OC language to properly store a reference to an object in a struct, especially if you are using APC.
Many other languages do not have a type like struct at all. Many languages such as Python, JavaScript, and so on are the only reference types that are considered "all objects". If you were to learn swift from that language, the concept might be more unfamiliar to you.
But don't worry! There is a region where almost all languages use value types: Numbers! The following example does not seem unfamiliar to programmers who have just started programming weeks, ignoring language:
var42 var x2 = x x++ print("x=\(x) x2=\(x2)") // prints: x=43 x2=42
It was so obvious and natural to us that we were unaware that he was doing something different, but it just showed up in front of us. As long as you are programming you are dealing with value types, even if you do not realize!
Many languages actually implement numbers as reference types, because they adhere to the philosophy of "everything is Object". However, they are immutable types, and the difference between value types and immutable reference types is difficult to detect. They behave like value types, even if they are not implemented as value types.
This is a fairly large portion of the understanding of value types and reference types. In terms of language semantics, their differences can only be affected when the data is changed. But if your data is immutable, then the difference between a value type and a reference type does not exist, at least the problem turns to performance rather than syntax.
This even appears in the tag pointer (tagged pointers) in OC. Just like in the tag pointer, an object is stored in the value of a pointer, which is a value type. Copy the storage area and copy the object. This difference is not obvious, because the OC Library is very careful to only add marker pointers to immutable types. Some Nsnumbers objects are reference types, others are value types, but this is no different.
Make a choice
Now that we know how the value type works, how do you choose your own data type?
Fundamentally, the difference between the two is what happens when you use the equals sign on them. The value type is copied, and the reference type has another reference.
So the fundamental question to ask when deciding which data type to use is: Is it meaningful to copy this type? Do you want to conveniently use copy operations and use them frequently?
Let us first look at some of the more extreme, obvious examples. Integers can be clearly copied, they should be of value type. Network sockets are obviously not copied, they should be reference types. X, y in point can be copied, and they should be of value type. A controller that represents a disk can obviously not be copied, they should be a reference type.
Some types can be copied but you don't want the copy to happen all the time. This indicates that they should be of a reference type. For example, a button on the screen should conceptually be able to be copied. But the Copy button is not exactly the same as the original one. The button you click on the copy does not trigger the original one. The copied button also does not occupy the position of the original button on the screen. That means your button should be a reference type.
View and window controllers are a similar example. They may be copied, which is extremely possible, but you almost never want to do that, so they should be reference types.
What about Model classes? You may have a user type that represents the users of your system, or a crime type that represents the action of a user. This must be a copy, so they should be of value type. However, you may want to update the user's actions somewhere in the program to another place in the program to make them visible. This means that your users should be managed by a user controller of a reference type.
The collection is an interesting example. They contain an array, a dictionary, and a string. Are they available for copying? It's obvious. Do you want the copy to be a convenient and frequent operation? That's not very clear.
Most languages say "no" to the problem and set their collections as reference types. This is true in OC, Java, Python, JavaScript, and any language I can think of (a major exception is the STL collection type in C + +, but C + + is a wonderful language, and it always behaves differently than everyone else).
Swift says "Yes" to this, which means that both array,dictionary and string are structs rather than classes. They are copied when they are assigned and are passed as parameters. This is definitely a wise decision if the cost of copying is small, and that's what Swift is trying to do.
Nested types
There are four different combinations when nesting values and reference types. Just one of these will make your life interesting.
If you have a reference type nested another reference type, nothing special happens. As usual, any pointer to an internal or external value can manipulate the object he points to. As long as one of the references manipulates the value to make it change, the value that the other reference points to is changed.
If you have a value type nested another value type, this effectively makes the memory area occupied by the value larger. Internal values are part of an external value. If you put an external value into a new storage space, all values including the internal values will be copied. If you put the internal values into a new storage space, only the internal values will be copied.
A reference type nesting a value type effectively expands the area of memory occupied by this reference type. Any pointer that points to an external value can manipulate everything, including nested internal values. Any changes to the internal values are visible to pointers that reference external values. If you put the internal value in a new storage area, a new value is copied from that store.
It's not that simple to nest a reference type for a value type. You can effectively break the value semantics without being aware of them. It may be good or bad, depending on how you do it. When you nest a reference type into a value type, the external value is copied when it is placed in a new area of memory, but the copied object still points to the original nested object. Here is an example:
class Inner { varvalue42 } struct Outer { varvalue42 var inner = Inner() } var outer = Outer() var outer2 = outer outer.value43 outer.inner.value43 print("outer2.value=\(outer2.value) outer2.inner.value=\(outer2.inner.value)”)
The printing results are as follows:
outer2.value=42 outer2.inner.value=43
Although Outer2 acquires a copy of value, it copies only the references of inner, so two structs share the same inner object. This will also affect the value of outer2.inner.value when changing the value of Outer.inner.value. Oh!
This behavior can be very useful. When you use caution, the structure you create has a write-time copy function (only if you execute Outer2.value = 43 o'clock to actually produce a copy, otherwise outer2 and outer still point to a common resource), and this efficient implementation of value semantics does not copy the data everywhere. The collection in Swift is doing this, and you can create one of these types yourself. To find out more please see let's Build swift.array.
This can also be dangerous. For example, we are creating a person object. This is a model class so it is obviously a copy, so it can be a struct. In the usual way, you set the name of the person class to the NSString type
struct Person { var name: NSString }
Then you create two person objects and create names in different parts:
name = NSMutableString() name.appendString("Bob") name.appendString(" ") name.appendString("Josephsonson") let bob = Person(namename) name.appendString(", Jr.") let bobjr = Person(namename)
Print these two names:
print(bob.name)print(bobjr.name)
Printing results:
Bob Josephsonson, Jr.Bob Josephsonson, Jr.
Oh!
What happened? Unlike Swift's string type, NSString is a reference type. It is immutable, but he has a mutable subclass, Nsmutablestring. When Bob is created, it creates a reference to the string held in name. Next, when that string is changed, this change is visible through Bob. Note This effectively changes Bob even if it is a constant type that is stored in a let statement. But it doesn't really change bob, it just changes the value that Bob refers to, but since that value is part of Bob's data, it looks like it changed bob in terms of language sense.
This kind of thing has been happening in OC all the time. Every OC programmer with some experience will have the habit of using a protective copy to modify properties everywhere. Since nsstring may actually be a nsmutablestring type, you can set the property to copy, or write a specific copy implementation in your own initialization method to avoid some problem generation. The same applies to mutable collection types.
In Swift, the conclusion here is simpler: use a value type instead of a reference type. In this example, name is used as the string type. Then you don't have to worry about sharing the references inadvertently.
Conclusion
Whenever you move a value type, he will be copied, and the reference type produces a new reference to the same underlying object. That means that the change to the reference type is visible to all other references, and changing the value type only affects the area of memory that you changed. When choosing which type to use, consider whether your type is appropriate to be copied and tend to use value types when the type is inherently available for copying. Finally, remember that if you embed a reference type in a value type, you'll get an error if you're not careful!
Original address
Swift: When to use structs and classes