Swift----方法 、 下标 、 继承 、 初始化 、 析构方法 、 可选链
下标的使用
1.1 问题
下标可以定义在类、结构体和枚举中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。
本案例定义一个Matrix结构体,用于呈现一个Double类型的二维矩阵,其结构体内部使用一个一维数组保存数据,并且定义一个下标用于判断是否会造成数组越界。
1.2 方案
首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。
Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值。
然后定义并实现下标运算,将传入的行号row和列号column转换为grid一维数组的下标,获取和设置对应的数据。
最后定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,通过断言进行判断。
1.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:定义Matrix结构体
首先定义一个Matrix结构体,该结构体有一个存储属性grid,是一个Double类型的结构体,用于存储矩阵的数据。
Matrix结构体另有两个整型的常量存储属性rows和columns,分别表示矩阵的行数和列数。通过init初始化方法对属性grid、rows以及columns赋初始值,代码如下所示:
struct Metrix {
//保存数据的一维数组
var grid : [Double]
//矩阵的行列
let rows : Int, columns : Int
//初始化方法
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = [Double](count: rows*columns, repeatedValue: 0.0)
}
}
步骤二:实现下标运算
定义并实现下标运算,Marix的下标运算需要两个整型参数row和column,表示二维矩阵的下标,通过行号 row和列号column转换为grid一维数组的下标,获取和设置对应的数据,代码如下所示:
struct Metrix {
//保存数据的一维数组
var grid : [Double]
//矩阵的行列
let rows : Int, columns : Int
//初始化方法
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = [Double](count: rows*columns, repeatedValue: 0.0)
}
subscript (row:Int,column:Int)->Double {
get {
return grid[row * columns + column]
}
set {
grid[row * columns + column] = newValue
}
}
}
步骤三:判断下标越界
在结构体中定义一个方法indexIsValidForRow,用于判断行号和列号是否越界,代码如下所示:
//判断下标越界
funcindexIsValidForRow (row:Int,column:Int)->Bool {
let index = row * columns + column
return row >= && row <self.rows&& column >= && column <self.columns
}
在下标运算中通过断言进行判断,代码如下所示:
subscript (row:Int,column:Int)->Double {
get {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
return grid[row * columns + column]
}
set {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
grid[row * columns + column] = newValue
}
}
然后创建一个Marix实例并进行赋值,运行结果如图-1所示:
图-
1.4 完整代码
本案例中,完整代码如下所示:
importUIKit
struct Metrix {
//保存数据的一维数组
var grid : [Double]
//矩阵的行列
let rows : Int, columns : Int
//初始化方法
init(rows:Int, columns:Int){
self.rows = rows
self.columns = columns
grid = [Double](count: rows*columns, repeatedValue: 0.0)
}
//判断下标越界
funcindexIsValidForRow (row:Int,column:Int)->Bool {
let index = row * columns + column
return row >= && row <self.rows&& column >= && column <self.columns
}
subscript (row:Int,column:Int)->Double {
get {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
return grid[row * columns + column]
}
set {
assert(self.indexIsValidForRow(row, column: column), "下标越界")
grid[row * columns + column] = newValue
}
}
}
var m = Metrix(rows: , columns: )
m[,] =
m[,] =
m[,] =
m[,] =
//下标越界
//m[0,4] = 500
m[,] =
m[,] =
隐藏
构造过程
2.1 问题
构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程,这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。本案例分别演示值类型的构造过程和类的构造过程。
2.2 方案
类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值,可以在在构造方法中为存储属性赋初始值,也可以在定义属性时为其设置默认值。构造方法以关键字init命名,最简单的形式是一个不带任何参数的实例方法。
在定义构造方法时也能提供参数,为构造过程中提供所需要的数据。
如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。
Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法,这个构造方法没有任何参数,并且将简单的创建一个所有属性都设置为默认值的实例。
构造方法可以通过其他构造方法来完成实例的部分构造过程成为构造委托,它能减少多个构造方法间的代码重复。构造委托的实现在值类型和类类型中有所不同,值类型不支持继承构造委托的过程相对简单。
由于类可以继承,所以类类型的构造委托需要保证其所有继承的存储型属性在构造时也能正确的初始化。
2.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:存储型属性的初始值
定义一个用来保存华氏温度的结构体Fahrenheit,拥有一个Double类型的存储属性temperature,通过构造方法给该属性赋初始值,代码如下所示:
struct Fahrenheit {
var temperature : Double
init(){
temperature = 32.0
}
}
创建一个Fahrenheit实例,构造方法会自动被调用,运行结果如图-2所示:
图-
步骤二:带参数的构造方法
定义一个包含摄氏度的结构体,包含两个不同的构造方法init(fromFahrenheit)和init(fromKelvin),分别通过接受不同的温度值来创建新的实例,代码如下所示:
struct Celsius {
vartemperatureInCelsius:Double = 0.0
init(fromFahrenheitfahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0)/1.8
}
init (fromKevinkelvin:Double) {
temperatureInCelsius = kelvin - 273.15
}
}
创建一个Celsius实例,如果不传参会调用构造方法init(),但是由于没有该方法则会编译报错,运行结果如图-3所示:
图-
步骤三:构造方法的内部参数名和外部参数名
如果在定义构造方法时没有提供参数的外部名称,Swift会为每个构造方法的参数自动生成一个跟内部名称相同的外部名。
定义一个结构体Color,包含三个Double类型的常量属性red、green、blue,分别表示红绿蓝的颜色数值。
Color结构体提供一个构造方法,包含三个Double类型的构造参数,代码如下所示:
struct Color {
let red, green, blue : Double
init(red:Double, green:Double, blue:Double){
self.red = red
self.green = green
self.blue = blue
}
}
创建一个Color实例时需要通过三种颜色的外部参数名来传值,如果不通过外部参数名字传值是无法调用该构造方法的,运行结果如图-4所示:
图-
当然也可以使用下划线来忽略外部参数名,代码如下所示:
struct Color {
let red, green, blue : Double
init(red:Double, green:Double, blue:Double){
self.red = red
self.green = green
self.blue = blue
}
init(_ red:Double, _ green:Double, _ blue:Double){
self.red = red
self.green = green
self.blue = blue
}
}
在创建Color实例时不通过外部参数名字传值将调用第二个构造方法,运行结果如图-5所示:
图-
步骤四:默认构造方法
Swift为所有属性已提供默认值的且自身没有定义任何构造方法的结构体或基类提供一个默认的构造方法。
定义一个类ShoppingListItem,封装了购物清单中的相关信息:名字name、数量quantity和购物状态purchased.
如果不为该类定义任何构造方法,它将自动获得一个可以为所有属性设置默认值的默认构造方法,对于可选类型的属性name将设置为nil,代码如下所示:
classShoppingListItem {
varname:String?
var quantity =
var purchased = false
}
var s = ShoppingListItem()
运行结果如图-6所示:
图-
如果是结构体,还可以自动获得一个逐一成员构造方法,该方法是用来初始化结构体新实例属性的快捷方法。
在调用逐一成员构造方法时通过与成员名相同的参数名进行传值来完成成员属性的初始化,代码如下所示:
struct Size {
var width = 0.0
var height = 0.0
}
var size = Size(width: , height: )
运行结果如图-7所示:
图-
步骤五:值类型的构造委托
对于值类型而言可以使用self.init在自定义的构造方法中引用其它的属于相同值类型的构造方法,并且只能在构造方法内部调用self.init。
定义一个结构体Rect用来代表几何矩形,包含一个Point类型的属性origin和一个Size类型的属性size,代码如下所示:
struct Point {
var x = 0.0
var y = 0.0
}
struct Size {
var width = 0.0
var height = 0.0
}
structRect {
var origin = Point()
var size = Size()
}
然后使用三种方式提供三个自定义的构造方法:
第一种使用默认值来初始化origin和size,在功能和自动获得的默认构造器是一样的,没有执行任何定制的构造过程;
第二方式使用特定的origin和size实例来初始化,在功能上跟自动获得的逐一成员构造器是一样的;
第三种使用特定的center和size来初始化,先通过 center和size的值计算出origin的坐标,然后再调用init(origin:size)构造方法来将新的origin和size的值赋值给相对应的属性。
代码如下所示:
structRect {
var origin = Point()
var size = Size()
init(){}
init(origin:Point,size:Size){
self.origin = origin
self.size = size
}
init(center:Point,size:Size){
letoriginX = center.x-size.width/
letoriginY = center.y-size.height/
self.init(origin: Point(x: originX, y: originY), size:size)
}
}
运行结果如图-8所示:
图-
步骤六:类类型的构造委托
Swift提供两种类型的类构造方法来确保所有类实例中存储属性都能获得初始值,分别是指定构造方法和便利构造方法。
定义三个类Food、RecipeIngredient以及ShoppingListItem,其中Food是基类包含一个String类型的name属性,并提供两个构造方法来创建Food实例,代码如下所示:
//类类型的构造委托
class Food {
var name :String
//指定构造方法
init(name :String) {
self.name = name
}
//便利构造方法
convenienceinit(){
self.init(name:"unnamed")
}
}
Food类提供了一个指定构造方法和一个没有参数的便利构造方法,由于Food是基类所以在指定构造方法不需要调用super.init()来完成构造,而便利构造方法则通过指定构造方法给新实例提供一个默认名称,运行结果如图-9所示:
图-
RecipeIngredient类是Food的子类,RecipeIngredient类构建了食谱中的一味调味剂,包含一个Int类型的属性quantity,并且定义了两个构造方法来创建RecipeIngredient,代码如下所示:
classRecipeIngredient : Food {
var quantity : Int
//指定构造器
init(name: String, quantity:Int) {
//必须先初始化本类定义的属性,才能调用父类的构造器
self.quantity = quantity
super.init(name: name)
//如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
//self.name = name
}
//便利构造器,且覆盖了父类的构造器
override convenience init(name: String) {
self.init(name:name, quantity:)
}
}
RecipeIngredient类的指定构造方法中调用父类的指定构造方法,RecipeIngredient类重写了父类的便利构造方法,并且在内部调用了类中的指定构造方法。
RecipeIngredient类的指定构造方法、便利构造方法以及父类的便利构造方法都可以用来创建RecipeIngredient类的新实例,运行结果如图-10所示:
图-
ShoppingListItem类是RecipeIngredient的子类,包含一个Bool类型的属性purchased,默认值是false。ShoppingListItem类另外还包含一个计算属性Description,代码如下所示:
classShoppingListItem : RecipeIngredient{
var purchased = false
var description : String {
var output = "\(self.quantity) x \(name)"
output += purchased ? "⎷" : "x"
return output
}
}
ShoppingListItem类的所有属性都有默认值,并且没有定义任何构造器,那么它将继承所有父类中的指定构造器和便利构造器,可以使用全部继承来的构造器创建新的实例,运行结果如图-11所示:
图-
2.4 完整代码
本案例中,完整代码如下所示:
importUIKit
//存储属性的初始化
struct Fahrenheit {
var temperature : Double
init(){
temperature = 32.0
}
}
var f = Fahrenheit()
f.temperature
//带参数的构造方法
struct Celsius {
vartemperatureInCelsius:Double = 0.0
init(fromFahrenheitfahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0)/1.8
}
init (fromKevinkelvin:Double) {
temperatureInCelsius = kelvin - 273.15
}
}
var c = Celsius(fromFahrenheit: )
c.temperatureInCelsius
var c2 = Celsius(fromKevin: )
c2.temperatureInCelsius
//构造方法的内部参数名和外部参数名
struct Color {
let red, green, blue : Double
init(red:Double, green:Double, blue:Double){
self.red = red
self.green = green
self.blue = blue
}
init(_ red:Double, _ green:Double, _ blue:Double){
self.red = red
self.green = green
self.blue = blue
}
}
let color = Color(red: , green: , blue: )
let color2 = Color(,,)
//默认构造方法
//class ShoppingListItem {
// varname:String?
// var quantity = 1
// var purchased = false
//}
//var s = ShoppingListItem()
//结构体逐一成员构造方法
struct Size {
var width = 0.0
var height = 0.0
}
var size = Size(width: , height: )
//值类型的构造委托
struct Point {
var x = 0.0
var y = 0.0
}
structRect {
var origin = Point()
var size = Size()
init(){}
init(origin:Point,size:Size){
self.origin = origin
self.size = size
}
init(center:Point,size:Size){
letoriginX = center.x-size.width/
letoriginY = center.y-size.height/
self.init(origin: Point(x: originX, y: originY), size:size)
}
}
varrect = Rect(center:Point(x: , y: ), size:Size(width:,height:))
rect.origin
rect.size
//类类型的构造委托
class Food {
var name :String
//指定构造方法
init(name :String) {
self.name = name
}
//便利构造方法
convenienceinit(){
self.init(name:"unnamed")
}
}
let meat = Food(name: "meat")
meat.name
let food = Food()
food.name
classRecipeIngredient : Food {
var quantity : Int
//指定构造器
init(name: String, quantity:Int) {
//必须先初始化本类定义的属性,才能调用父类的构造器
self.quantity = quantity
super.init(name: name)
//如果需要在子类中给继承来的属性赋值,需要写在super.init的后面
//self.name = name
}
//便利构造器,且覆盖了父类的构造器
override convenience init(name: String) {
self.init(name:name, quantity:)
}
}
let r1 = RecipeIngredient()
let r2 = RecipeIngredient(name: "面")
let r3 = RecipeIngredient(name: "辣椒", quantity:)
classShoppingListItem : RecipeIngredient{
var purchased = false
var description : String {
var output = "\(self.quantity) x \(name)"
output += purchased ? "⎷" : "x"
return output
}
}
let item1 = ShoppingListItem()
let item2 = ShoppingListItem(name: "苹果")
let item3 = ShoppingListItem(name: "泡面", quantity: )
Swift----方法 、 下标 、 继承 、 初始化 、 析构方法 、 可选链的更多相关文章
- python - class内置方法 doc/module/del(析构方法)/cal 方法
__doc__ # __doc__ #摘要信息 #这个属性不会继承给子类 class Test(): """这是摘要信息""" pass x ...
- 【Swift学习】Swift编程之旅---析构方法(十九)
在一个类的实例内存被释放之前,析构方法被立即调用.使用deinit关键字来声明析构方法,类似于构造方法用init来声明.析构方法只适用于类类型. 析构方法原理 Swift会自动释放不再需要的实例以 ...
- C++ 析构方法
1.什么是析构方法? 析构方法与构造方法互补. 2.为什么设计析构方法? 构造方法创建一个对象,对象内部往往还会申请一些资源.设计析构方法的目的是 释放资源,同时销毁自身. 3.析构方法可以认为分为两 ...
- 【M11】禁止异常流出析构方法之外
1.在两种情况下,调用析构方法:a.在正常状态下被销毁,栈上的对象离开作用域或者堆上的对象执行delete:b.抛出异常,堆栈回滚,栈上已经构造好的对象,也就是抛出异常之前的代码,自动调用析构方法.注 ...
- PHP 构造方法 __construct()和PHP 析构方法 __destruct()
PHP 构造方法 __construct() 允许在实例化一个类之前先执行构造方法. 构造方法 构造方法是类中的一个特殊方法.当使用 new 操作符创建一个类的实例时,构造方法将会自动调用,其名称必须 ...
- C#资源释放及Dispose、Close和析构方法
https://www.cnblogs.com/luminji/archive/2011/01/05/1926468.html C#资源释放及Dispose.Close和析构方法 备注:此文的部分 ...
- 【Swift学习】Swift编程之旅---可选链(二十一)
可选链Optional Chaining是一种可以在当前值可能为nil的可选值上请求和调用属性.方法及下标的方法.如果可选值有值,那么调用就会成功:如果可选值是nil,那么调用将返回nil.多个调用可 ...
- Swift 可选链
可选链(Optional Chaining)是一种可以请求和调用属性.方法和子脚本的过程,用于请求或调用的目标可能为nil. 可选链返回两个值: 如果目标有值,调用就会成功,返回该值 如果目标为nil ...
- swift 学习- 19 -- 可选链式调用
// 可选链式调用 是一种在当前值可能为 nil 的可选值上请求 和 调用属性, 方法以及下标, 如果 可选值有值, 那么调用就会成功, 如果可选值是 nil, 那么就会将返回 nil , // 多个 ...
- PHP其它常用函数;<<<面向对象(OPP)的三大特性:封装、继承、加态:>>> <----面试题 ;构造方法、析构方法,魔术方法、set、get方法;静态;抽象类;接口
PHP其它常用函数: 赋值:$r->name = "元素"; 取值: echo $r->name; count() 计算数组中的元素数目或对象中 ...
随机推荐
- 16061701(地图灯光编译Beast报错)
[目标] 地图灯光编译报错 [思路] 1 我自己测c2_cwd_rt 附件为当时log 2 ExampleGame\BeastCache\PersistentCache 3 重新删除掉BeastCac ...
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
- uva 213 Message Decoding
思路来自紫书...开始时的思路估计100行+,果断放弃!关键:1.正确提取出函数! initmap():初始化字母与整数的映射. returnint(x):向后读取x位,并转换为十进制数返回. ...
- Spring Shedule Task之注解实现 (两次启动Schedule Task 的解决方案)
在spring 中的新引入的task 命名空间.可以部分取代 quartz 功能,配置和API更加简单,并且支持注解方式. 第一步: 在Spring的相关配置文件中(applicationContex ...
- STM32——CAN通讯实现
CAN通讯的实现步骤: 1.CAN初始化,其中包括:a.配置CAN时钟,配置IO: b.使能CAN中断向量: c.CAN硬件寄存器配置初始化: d.过滤器初始化: e.打开CAN中断. 2.CAN发送 ...
- 八皇后java算法
import java.util.Date; public class EightQueen { public static void main(String[] args) { long star ...
- NGUI如何使2D图片按像素1:1显示在屏幕上
NGUI版本为3.5.1. 将camera 设置为正交模式,size值设为1. UIRoot(2D)有3种缩放样式: 1.PixelPerfect.UI严格按照指定的像素大小显示,不会随着屏幕的分辨率 ...
- 关于编程一些鲜为人知的真相 csdn
<关于编程一些鲜为人知的真相>一文讲了一些编程的真相:如果把所有项目的生命周期平均一下,那么一个程序员大概10-20%的时间用来写代码,并且大多数程序员可能每天大约只有10-12行代码会进 ...
- Unity中创建二维码
在网络上发现了一个可以把字符串转换成二维码的dll,但是我们要怎么使用他呢.不废话,直接进入主题. 用到的引用 using UnityEngine;using ZXing;using ZXing.Qr ...
- 关于docker容器是怎样建立新的namespace的。
最近博客收到了一封交流的私信,感谢您的关注:现在就我理解的docker建立容器时namespace的建立问题做一个 个人的回答: 一,从原理角度来讲: docker创建container,说白了就是l ...