1 1 下标的使用
2
3 1.1 问题
4
5 下标可以定义在类、结构体和枚举中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。
6
7 本案例定义一个Matrix结构体,用于呈现一个Double类型的二维矩阵,其结构体内部使用一个一维数组保存数据,并且定义一个下标用于判断是否会造成数组越界。
8
9 1.2 方案
10
11 首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。
12
13 Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值。
14
15 然后定义并实现下标运算,将传入的行号row和列号column转换为grid一维数组的下标,获取和设置对应的数据。
16
17 最后定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,通过断言进行判断。
18
19 1.3 步骤
20
21 实现此案例需要按照如下步骤进行。
22
23 步骤一:定义Matrix结构体
24
25 首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。
26
27 Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值,代码如下所示:
28
29
30 struct Metrix {
31 //保存数据的一维数组
32 var grid : [Double]
33 //矩阵的行列
34 let rows : Int, columns : Int
35 //初始化方法
36 init(rows:Int, columns:Int){
37 self.rows = rows
38 self.columns = columns
39 grid = [Double](count: rows*columns, repeatedValue: 0.0)
40 }
41 }
42 步骤二:实现下标运算
43
44 定义并实现下标运算,Marix的下标运算需要两个整型参数row和column,表示二维矩阵的下标,通过行号 row和列号column转换为grid一维数组的下标,获取和设置对应的数据,代码如下所示:
45
46
47 struct Metrix {
48 //保存数据的一维数组
49 var grid : [Double]
50 //矩阵的行列
51 let rows : Int, columns : Int
52 //初始化方法
53 init(rows:Int, columns:Int){
54 self.rows = rows
55 self.columns = columns
56 grid = [Double](count: rows*columns, repeatedValue: 0.0)
57 }
58 subscript (row:Int,column:Int)->Double {
59 get {
60 return grid[row * columns + column]
61 }
62 set {
63 grid[row * columns + column] = newValue
64 }
65 }
66 }
67 步骤三:判断下标越界
68
69 在结构体中定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,代码如下所示:
70
71
72 //判断下标越界
73 funcindexIsValidForRow (row:Int,column:Int)->Bool {
74 let index = row * columns + column
75 return row >= 0 && row <self.rows&& column >= 0 && column <self.columns
76 }
77 在下标运算中通过断言进行判断,代码如下所示:
78
79
80 subscript (row:Int,column:Int)->Double {
81 get {
82 assert(self.indexIsValidForRow(row, column: column), "下标越界")
83 return grid[row * columns + column]
84 }
85 set {
86 assert(self.indexIsValidForRow(row, column: column), "下标越界")
87 grid[row * columns + column] = newValue
88 }
89 }
90 然后创建一个Marix实例并进行赋值,运行结果如图-1所示:
91
92
93 图-1
94
95 1.4 完整代码
96
97 本案例中,完整代码如下所示:
98
99
100 importUIKit
101 struct Metrix {
102 //保存数据的一维数组
103 var grid : [Double]
104 //矩阵的行列
105 let rows : Int, columns : Int
106 //初始化方法
107 init(rows:Int, columns:Int){
108 self.rows = rows
109 self.columns = columns
110 grid = [Double](count: rows*columns, repeatedValue: 0.0)
111 }
112 //判断下标越界
113 funcindexIsValidForRow (row:Int,column:Int)->Bool {
114 let index = row * columns + column
115 return row >= 0 && row <self.rows&& column >= 0 && column <self.columns
116 }
117 subscript (row:Int,column:Int)->Double {
118 get {
119 assert(self.indexIsValidForRow(row, column: column), "下标越界")
120 return grid[row * columns + column]
121 }
122 set {
123 assert(self.indexIsValidForRow(row, column: column), "下标越界")
124 grid[row * columns + column] = newValue
125 }
126 }
127 }
128 var m = Metrix(rows: 3, columns: 4)
129 m[0,0] = 100
130 m[0,1] = 200
131 m[0,2] = 300
132 m[0,3] = 400
133 //下标越界
134 //m[0,4] = 500
135 m[1,0] = 600
136 m[1,1] = 700
137 隐藏
138
139 2 构造过程
140
141 2.1 问题
142
143 构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程,这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。本案例分别演示值类型的构造过程和类的构造过程。
144
145 2.2 方案
146
147 类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值,可以在在构造方法中为存储属性赋初始值,也可以在定义属性时为其设置默认值。构造方法以关键字init命名,最简单的形式是一个不带任何参数的实例方法。
148
149 在定义构造方法时也能提供参数,为构造过程中提供所需要的数据。
150
151 如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。
152
153 Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法,这个构造方法没有任何参数,并且将简单的创建一个所有属性都设置为默认值的实例。
154
155 构造方法可以通过其他构造方法来完成实例的部分构造过程成为构造委托,它能减少多个构造方法间的代码重复。构造委托的实现在值类型和类类型中有所不同,值类型不支持继承构造委托的过程相对简单。
156
157 由于类可以继承,所以类类型的构造委托需要保证其所有继承的存储型属性在构造时也能正确的初始化。
158
159 2.3 步骤
160
161 实现此案例需要按照如下步骤进行。
162
163 步骤一:存储型属性的初始值
164
165 定义一个用来保存华氏温度的结构体Fahrenheit,拥有一个Double类型的存储属性temperature,通过构造方法给该属性赋初始值,代码如下所示:
166
167
168 struct Fahrenheit {
169 var temperature : Double
170 init(){
171 temperature = 32.0
172 }
173 }
174 创建一个Fahrenheit实例,构造方法会自动被调用,运行结果如图-2所示:
175
176
177 图-2
178
179 步骤二:带参数的构造方法
180
181 定义一个包含摄氏度的结构体,包含两个不同的构造方法init(fromFahrenheit)和init(fromKelvin),分别通过接受不同的温度值来创建新的实例,代码如下所示:
182
183
184 struct Celsius {
185 vartemperatureInCelsius:Double = 0.0
186 init(fromFahrenheitfahrenheit:Double) {
187 temperatureInCelsius = (fahrenheit - 32.0)/1.8
188 }
189 init (fromKevinkelvin:Double) {
190 temperatureInCelsius = kelvin - 273.15
191 }
192 }
193 创建一个Celsius实例,如果不传参会调用构造方法init(),但是由于没有该方法则会编译报错,运行结果如图-3所示:
194
195
196 图-3
197
198 步骤三:构造方法的内部参数名和外部参数名
199
200 如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。
201
202 定义一个结构体Color,包含三个Double类型的常量属性red、green、blue,分别表示红绿蓝的颜色数值。
203
204 Color结构体提供一个构造方法,包含三个Double类型的构造参数,代码如下所示:
205
206
207 struct Color {
208 let red, green, blue : Double
209 init(red:Double, green:Double, blue:Double){
210 self.red = red
211 self.green = green
212 self.blue = blue
213 }
214 }
215 创建一个Color实例时需要通过三种颜色的外部参数名来传值,如果不通过外部参数名字传值是无法调用该构造方法的,运行结果如图-4所示:
216
217
218 图-4
219
220 当然也可以使用下划线来忽略外部参数名,代码如下所示:
221
222
223 struct Color {
224 let red, green, blue : Double
225 init(red:Double, green:Double, blue:Double){
226 self.red = red
227 self.green = green
228 self.blue = blue
229 }
230 init(_ red:Double, _ green:Double, _ blue:Double){
231 self.red = red
232 self.green = green
233 self.blue = blue
234 }
235 }
236 在创建Color实例时不通过外部参数名字传值将调用第二个构造方法,运行结果如图-5所示:
237
238
239 图-5
240
241 步骤四:默认构造方法
242
243 Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法。
244
245 定义一个类ShoppingListItem,封装了购物清单中的相关信息:名字name、数量quantity和购物状态purchased.
246
247 如果不为该类定义任何构造方法,它将自动获得一个可以为所有属性设置默认值的默认构造方法,对于可选类型的属性name将设置为nil,代码如下所示:
248
249
250 classShoppingListItem {
251 varname:String?
252 var quantity = 1
253 var purchased = false
254 }
255 var s = ShoppingListItem()
256 运行结果如图-6所示:
257
258
259 图-6
260
261 如果是结构体,还可以自动获得一个逐一成员构造方法,该方法是用来初始化结构体新实例属性的快捷方法。
262
263 在调用逐一成员构造方法时通过与成员名相同的参数名进行传值来完成成员属性的初始化,代码如下所示:
264
265
266 struct Size {
267 var width = 0.0
268 var height = 0.0
269 }
270 var size = Size( 10, height: 20)
271 运行结果如图-7所示:
272
273
274 图-7
275
276 步骤五:值类型的构造委托
277
278 对于值类型而言可以使用self.init在自定义的构造方法中引用其它的属于相同值类型的构造方法,并且只能在构造方法内部调用self.init。
279
280 定义一个结构体Rect用来代表几何矩形,包含一个Point类型的属性origin和一个Size类型的属性size,代码如下所示:
281
282
283 struct Point {
284 var x = 0.0
285 var y = 0.0
286 }
287 struct Size {
288 var width = 0.0
289 var height = 0.0
290 }
291 structRect {
292 var origin = Point()
293 var size = Size()
294 }
295 然后使用三种方式提供三个自定义的构造方法:
296
297 第一种使用默认值来初始化origin和size,在功能和自动获得的默认构造器是一样的,没有执行任何定制的构造过程;
298
299 第二方式使用特定的origin和size实例来初始化,在功能上跟自动获得的逐一成员构造器是一样的;
300
301 第三种使用特定的center和size来初始化,先通过 center和size的值计算出origin的坐标,然后再调用init(origin:size)构造方法来将新的origin和size的值赋值给相对应的属性。
302
303 代码如下所示:
304
305
306 structRect {
307 var origin = Point()
308 var size = Size()
309 init(){}
310 init(origin:Point,size:Size){
311 self.origin = origin
312 self.size = size
313 }
314 init(center:Point,size:Size){
315 letoriginX = center.x-size.width/2
316 letoriginY = center.y-size.height/2
317 self.init(origin: Point(x: originX, y: originY), size:size)
318 }
319 }
320 运行结果如图-8所示:
321
322
323 图-8
324
325 步骤六:类类型的构造委托
326
327 Swift提供两种类型的类构造方法来确保所有类实例中存储属性都能获得初始值,分别是指定构造方法和便利构造方法。
328
329 定义三个类Food、RecipeIngredient以及ShoppingListItem,其中Food是基类包含一个String类型的name属性,并提供两个构造方法来创建Food实例,代码如下所示:
330
331
332 //类类型的构造委托
333 class Food {
334 var name :String
335 //指定构造方法
336 init(name :String) {
337 self.name = name
338 }
339 //便利构造方法
340 convenienceinit(){
341 self.init(name:"unnamed")
342 }
343 }
344 Food类提供了一个指定构造方法和一个没有参数的便利构造方法,由于Food是基类所以在指定构造方法不需要调用super.init()来完成构造,而便利构造方法则通过指定构造方法给新实例提供一个默认名称,运行结果如图-9所示:
345
346
347 图-9
348
349 RecipeIngredient类是Food的子类,RecipeIngredient类构建了食谱中的一味调味剂,包含一个Int类型的属性quantity,并且定义了两个构造方法来创建RecipeIngredient,代码如下所示:
350
351
352 classRecipeIngredient : Food {
353 var quantity : Int
354 //指定构造器
355 init(name: String, quantity:Int) {
356 //必须先初始化本类定义的属性,才能调用父类的构造器
357 self.quantity = quantity
358 super.init(name: name)
359 //如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
360 //self.name = name
361 }
362 //便利构造器,且覆盖了父类的构造器
363 override convenience init(name: String) {
364 self.init(name:name, quantity:1)
365 }
366 }
367 RecipeIngredient类的指定构造方法中调用父类的指定构造方法,RecipeIngredient类重写了父类的便利构造方法,并且在内部调用了类中的指定构造方法。
368
369 RecipeIngredient类的指定构造方法、便利构造方法以及父类的便利构造方法都可以用来创建RecipeIngredient类的新实例,运行结果如图-10所示:
370
371
372 图-10
373
374 ShoppingListItem类是RecipeIngredient的子类,包含一个Bool类型的属性purchased,默认值是false。ShoppingListItem类另外还包含一个计算属性Description,代码如下所示:
375
376
377 classShoppingListItem : RecipeIngredient{
378 var purchased = false
379 var description : String {
380 var output = "(self.quantity) x (name)"
381 output += purchased ? "⎷" : "x"
382 return output
383 }
384 }
385 ShoppingListItem类的所有属性都有默认值,并且没有定义任何构造器,那么它将继承所有父类中的指定构造器和便利构造器,可以使用全部继承来的构造器创建新的实例,运行结果如图-11所示:
386
387
388 图-11
389
390 2.4 完整代码
391
392 本案例中,完整代码如下所示:
393
394
395 importUIKit
396 //存储属性的初始化
397 struct Fahrenheit {
398 var temperature : Double
399 init(){
400 temperature = 32.0
401 }
402 }
403 var f = Fahrenheit()
404 f.temperature
405 //带参数的构造方法
406 struct Celsius {
407 vartemperatureInCelsius:Double = 0.0
408 init(fromFahrenheitfahrenheit:Double) {
409 temperatureInCelsius = (fahrenheit - 32.0)/1.8
410 }
411 init (fromKevinkelvin:Double) {
412 temperatureInCelsius = kelvin - 273.15
413 }
414 }
415 var c = Celsius(fromFahrenheit: 88)
416 c.temperatureInCelsius
417 var c2 = Celsius(fromKevin: 100)
418 c2.temperatureInCelsius
419 //构造方法的内部参数名和外部参数名
420 struct Color {
421 let red, green, blue : Double
422 init(red:Double, green:Double, blue:Double){
423 self.red = red
424 self.green = green
425 self.blue = blue
426 }
427 init(_ red:Double, _ green:Double, _ blue:Double){
428 self.red = red
429 self.green = green
430 self.blue = blue
431 }
432 }
433 let color = Color(red: 10, green: 10, blue: 10)
434 let color2 = Color(10,20,30)
435 //默认构造方法
436 //class ShoppingListItem {
437 // varname:String?
438 // var quantity = 1
439 // var purchased = false
440 //}
441 //var s = ShoppingListItem()
442 //结构体逐一成员构造方法
443 struct Size {
444 var width = 0.0
445 var height = 0.0
446 }
447 var size = Size( 10, height: 20)
448 //值类型的构造委托
449 struct Point {
450 var x = 0.0
451 var y = 0.0
452 }
453 structRect {
454 var origin = Point()
455 var size = Size()
456 init(){}
457 init(origin:Point,size:Size){
458 self.origin = origin
459 self.size = size
460 }
461 init(center:Point,size:Size){
462 letoriginX = center.x-size.width/2
463 letoriginY = center.y-size.height/2
464 self.init(origin: Point(x: originX, y: originY), size:size)
465 }
466 }
467 varrect = Rect(center:Point(x: 20, y: 20), size:Size(20,height:30))
468 rect.origin
469 rect.size
470 //类类型的构造委托
471 class Food {
472 var name :String
473 //指定构造方法
474 init(name :String) {
475 self.name = name
476 }
477 //便利构造方法
478 convenienceinit(){
479 self.init(name:"unnamed")
480 }
481 }
482 let meat = Food(name: "meat")
483 meat.name
484 let food = Food()
485 food.name
486 classRecipeIngredient : Food {
487 var quantity : Int
488 //指定构造器
489 init(name: String, quantity:Int) {
490 //必须先初始化本类定义的属性,才能调用父类的构造器
491 self.quantity = quantity
492 super.init(name: name)
493 //如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
494 //self.name = name
495 }
496 //便利构造器,且覆盖了父类的构造器
497 override convenience init(name: String) {
498 self.init(name:name, quantity:1)
499 }
500 }
501 let r1 = RecipeIngredient()
502 let r2 = RecipeIngredient(name: "面")
503 let r3 = RecipeIngredient(name: "辣椒", quantity:5)
504 classShoppingListItem : RecipeIngredient{
505 var purchased = false
506 var description : String {
507 var output = "(self.quantity) x (name)"
508 output += purchased ? "⎷" : "x"
509 return output
510 }
511 }
512 let item1 = ShoppingListItem()
513 let item2 = ShoppingListItem(name: "苹果")
514 let item3 = ShoppingListItem(name: "泡面", quantity: 10)