-
Notifications
You must be signed in to change notification settings - Fork 196
/
Copy pathsection-1.swift
301 lines (270 loc) · 9.94 KB
/
section-1.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// ------------------------------------------------------------------------------------------------
// Things to know:
//
// * Properties store values in classes, structures and enumerations.
// ------------------------------------------------------------------------------------------------
// Here's a structure with a couple simple stored properties:
struct FixedLengthRange
{
var firstValue: Int
let length: Int
}
// Structures must have all of their properties initialized upon instantiation.
//
// This won't compile since the struct includes properties that havn't been initialized with
// default values:
//
// var anotherRangeOfThreeItems = FixedLengthRange()
//
// In order to instantiate a FixedLengthRange struct, we must use the memberwise initializer. Note
// that this will initialize a constant property and a variable property inside the struct:
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
rangeOfThreeItems.firstValue = 6
// ------------------------------------------------------------------------------------------------
// Lazy Stored Properties
//
// A Lazy Stored Property is a value that is not calculated until its first use.
//
// They are declared using the "lazy" attribute and may not be constant.
//
// Global and local variables are all lazy, except that they don't need the lazy attribute.
//
// Here, we'll define a couple classes to showcase Lazy Stored Properties. In this example, let's
// assume that DataImporter is a time-consuming process, and as such, we will want to use a lazy
// stored property whenever we use it. This way, if we end up never using it, we never pay the
// penalty of instantiating it.
class DataImporter
{
var filename = "data.txt"
}
class DataManager
{
lazy var importer = DataImporter()
var data = [String]()
}
// Now let's instantiate the data manager and add some simple data to the class:
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// Notice how we haven't used the importer yet, so it is nil:
manager
// So now let's access it:
manager.importer.filename
// And now we see the importer was created:
manager
// ------------------------------------------------------------------------------------------------
// Computed Properties
//
// Computed properties don't store data, but rather use getters and setters for accessing values
// that are computed up on request.
//
// Computed Properties are available for global as well as local variables.
//
// We'll start with a few structures that we'll use to show how computed properties work.
struct Point
{
var x = 0.0, y = 0.0
}
struct Size
{
var width = 0.0, height = 0.0
}
// The following structure includes a computed property with a Point type named 'center'. Notice
// that 'center' is variable (not constant) which is a requirement of computed properties.
//
// Every computed property must have a getter, but does not need a setter. If we provide a setter,
// we need to know the new value being assigned to the computed property. We've called this
// value 'newCenter'. Note that this value does not have a type annotation because the computed
// property already knows that it is a Point type. Providing a type annotation would be an error.
struct Rect
{
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(newCenter)
{
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
// Here, we'll create a square from our Rect structure
var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
// We can now get the center point, computed from the Rect's origin and size. Being a computed
// property, we can treat it just like any other peroperty.
let initialSquareCenter = square.center
// Since we provided a setter, we can also set the center point as if it is a stored property.
// This will effectively update the Rect's origin and size based on the specified center point.
square.center = Point(x: 15, y: 15)
// We can see that the origin has been updated from (0, 0) to (10, 10):
square.origin
// Shorthand Setter Declaration
//
// The computed property's setter from the Rect structure provided a parameter on the setter named
// 'newCenter'. If we don't specify this parameter, Swift will automatically generate an input
// value named 'newValue' for us.
//
// Here, AlternativeRect is the same declaration as Rect above, except that the setter uses
// Swift's default setter value, 'newValue':
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)
}
}
}
// We can also have a read-only computed property by simply omitting the setter:
struct Cube
{
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double
{
get
{
return width * height * depth
}
}
}
// Alternatively, Swift allows us to shorten the syntax of a read-only computed property by
// omitting the get{} construct and inserting the code for the getter directly into the property
// declaration:
struct AnotherCube
{
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double
{
return width * height * depth
}
}
// Let's declare our structure and read the 'volume' property
var cube = AnotherCube(width: 4, height: 5, depth: 2)
cube.volume
// Since the 'volume' property is read-only, if we tried to assign a value to it, it would
// would generate a compiler error.
//
// The following line of code will not compile:
//
// cube.volume = 8.0
// ------------------------------------------------------------------------------------------------
// Property Observers
//
// Property observers allow us to respond to changes in a property's value. We can declare an
// observer that contains our code that is run just before a property is set (optionally allowing
// us to alter the value being set) and an observer that is run just after a property has been
// modified.
//
// Property observers are available for global as well as local variables.
//
// These observers are not called when a property is first initialized, but only when it changes.
//
// Similar to setters, each 'willSet' and 'didSet' will receive a parameter that represents (in
// the case of 'willSet') the value about to be assigned to the property and (in the case of
// 'didSet') the value that was previously stored in the property prior to modification.
class StepCounter
{
var totalSteps: Int = 0
{
willSet(newTotalSteps)
{
"About to step to \(newTotalSteps)"
}
didSet(oldTotalSteps)
{
"Just stepped from \(oldTotalSteps)"
}
}
}
// Let's create an instance of StepCounter so we can try out our observer
let stepCounter = StepCounter()
// The following will first call 'willSet' on the 'totalSteps' property, followed by a change to
// the value itself, followed by a call to 'didSet'
stepCounter.totalSteps = 200;
// Similar to setters, we can shorten our observers by omitting the parameter list for each. When
// we co this, Swift automatically provides parameters named 'newValue' and 'oldValue'
class StepCounterShorter
{
var totalSteps: Int = 0
{
willSet{ "About to step to \(newValue)" }
didSet { "Just stepped from \(oldValue)" }
}
}
// We can also override the value being set by modifying the property itself within the 'didSet'
// observer. This only works in the 'didSet'. If you attempt to modify the property in 'willSet'
// you will receive a compiler warning.
//
// Let's try wrapping our value to the range of 0...255
class StepCounterShorterWithModify
{
var totalSteps: Int = 0
{
willSet{ "About to step to \(newValue)" }
didSet { totalSteps = totalSteps & 0xff }
}
}
var stepper = StepCounterShorterWithModify()
stepper.totalSteps = 345
stepper.totalSteps // This reports totalSteps is now set to 89
// ------------------------------------------------------------------------------------------------
// Type Properties
//
// Until now, we've been working with Instance Properties and Instance Methods, which are
// associated to an instance of a class, structure or enumeration. Each instance gets its own copy
// of the property or method.
//
// Type properties are properties that are attached to the class, structure or enumeration's type
// itself and not any specific instance. All instances of a type will share the same Type Property.
//
// These are similar to 'static' properties in C-like languages.
//
// For Value types (structs, enums) we use the 'static' keyword. For Class types with the 'class'
// keyword.
//
// Type properties can be in the form of stored properties or computed properties. As with normal
// stored or computed properties, all the same rules apply except that stored type properties must
// always have a default value. This exception is necessary because the initializer (which we'll
// learn about later) is associated to a specific instance and as such, cannot be reliable for
// initializing type properties.
//
// Here is a class with a couple of type properties
struct SomeStructure
{
static var storedTypeProperty = "some value"
// Here's a read-only computed type property using the short-hand read-only syntax
static var computedTypeProperty: Int { return 4 }
}
// Similarly, here's an enumeration with a couple of type properties
enum SomeEnum
{
static let storedTypeProperty = "some value"
static var computedTypeProperty: Int { return 4 }
}
// Classes are a little different in that they cannot contain stored type properties, but may
// only contain computed type properties
class SomeClass
{
// The following line won't compile because classes aren't allowed stored type properties
//
// class var storedTypeProperty = "some value"
// This is read-only, but you can also do read/write
class var computedTypeProperty: Int { return 4 }
}