Swift----方法 、 下标 、 继承 、 初始化 、 析构方法 、 可选链

来源:转载

 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(width: 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(width: 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(width: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)

 

分享给朋友:
您可能感兴趣的文章:
随机阅读: