前文介绍了可选类型和闭包,这两个概念在做Object-C时没有遇到,转做Swift又必须面对的。当然除了上述两个概念外,还有构造函数、下标、计算属性等等。这次我们就分别介绍下。
构造函数
构造函数是一种特殊的函数,主要用途是在创建对象时,初始化对象和分配空间,并为对象成员变量进行初始值。在Swift语言中,所有的构造函数由init来实现。
构造函数分为以下几种:
-
自定义构造函数
-
便利构造函数
-
可失败构造函数
下面从这几方面分别介绍
自定义构造函数
除了系统提供的init默认构造函数外,Swift还支持自定义构造函数。自定义构造函数分为必选(no-Optional)和可选(Optional)属性构造函数。下面分别说明。
必选(no-Optional)属性构造函数
必选属性构造函数,顾名思义,就是成员属性的类型是基本类型的构造函数。
必选属性构造函数有些什么注意点呢?
举例来说明。
//自定义Student类
class Student {
//姓名
var name:String
//年纪
var age:Int
}
//实例化
var st = Student()
上述代码,编写完成后,就会报两个错误。如下图所示:
原因:类中定义了必选属性,但是没有赋初值。所以,对于必选构造函数来说,必须通过构造函数为这些必选属性分配空间并且设置初始值。
那上面的错误怎么解决呢?通过对Student进行初始化,就解决了错误。如下代码所示。
class Student {
var name:String
var age:Int
init() {
name = ""
age = 0
}
}
var st = Student()
子类构造函数
上述一个简单的类,没有子类继承,问题就简单一点。若是有子类继承的情况,子类构造函数又会怎么样的呢?
我们就针对Student类,派生一个子类Schoolchild(小学生)。Schoolchild继承Student类,并且按Student类方式,初始化好成员属性(grade),如下代码:
class Student {
//姓名
var name:String
//年龄
var age:Int
init() {
name = ""
age = 0
}
}
var st = Student()
class Schoolchild:Student {
//年级
var grade:Int
init() {
grade = 0
}
}
上述代码在实际项目中是编译失败的,错误如下图所示:
原因是,Schoolchild继承Student类,init方法在父类已经实现了,子类又重复实现init方法。要解决这个报错,要么重载init函数,要么重写init函数。如下所示。
//重写方式,重写(override)就是方法名,参数和返回值跟父类的一样。
override init() {
grade = 0
}
//重载方式,重载(overload)就是方法名一样,参数或返回值不一样。
init(aGrade:Int) {
grade = aGrade
}
通过重载或者重写,解决了上面的问题。下面在看看构造函数的调用时机。
子类调用父类构造函数的时机
在子类构造函数,会调用super.init()来完成父类的初始化。那问题来了,super.init是在什么时机调用呢?子类属性初始化前调用还是之后调用呢?
如下代码所示:
override init() {
super.init() //在前调用
grade = 0
}
override init() {
grade = 0
super.init() //在后调用
}
实际情况是,在初始化之后调用是正确的。在前调用会报错。错误如下:
原因就是:Swift的规则是先初始化好子类属性,再初始化父类的属性。可以理解:先初始化好子类数据,再初始化上一级(父亲)数据,这样一层层往上递归。
再问个问题,子类和父类的析构过程又是怎么样的呢?通过实例证明,先析构子类,再析构父类。
小小结:
1.自定义子类时,需要在构造函数中,首先为本类定义的属性设置初始值。
2.然后再调用父类的构造函数,初始化父类中定义的属性。
3.析构时,先析构子类,再析构父类。
可选(Optional)属性的构造函数
上述是针对必选类型的构造函数,那么可选属性的构造函数有是什么情况?
自定义Student2类,不过name和age为可选类型
class Student2 {
//姓名
var name:String?
//年纪
var age:Int?
}
//实例化
var st = Student2()
上述Student2在实际项目中是能编译通过的,虽然没有实现init构造函数。那为什么不写init构造函数就能编译通过呢?原因:
1.可选属性不需要设置初始值,默认都是nil。
2.可选属性在设置数值的时候才分配空间的,是延迟分配空间。
自定义构造函数小结
1.非Optional属性,都必须在构造函数中设置初始值,从而保证对象在被实例化的时候,属性都被正确初始化。
2.在调用父类构造函数前,须保证本类的属性都已经完成初始化。
3.Swift中的构造函数不用写func。
4.可选属性的类不必实现构造函数方法。
默认属性值
在构造函数中,还有一写法:默认属性值,前面讲到的是在构造函数(init函数)中为存储型属性设置初始值;其实,还可以在属性声明时为其设置默认值。这样做的好处:能让构造函数更简洁、更清晰,并通过默认值来自动推导出属性的类型。
class Student3 {
// 设置默认值
//姓名
var name = "name"
//年龄
var age = 10
//好处:不用再实现init构造函数,同时不用声明类型,
//编译器自动推断出类型。
}
var st3 = Student3()
convenience便利构造函数
1.大部分情况下,我们都是使用自定义构造函数,但在某特定情况下,也会编写便利构造函数。
2.由convenience 关键字修饰的构造函数就是便利构造函数。
3.便利构造函数针对某些场景特别使用,具有一定的方便性。究其实质是简化自定义构造函数。
便利构造函数的特点
1.可以返回 nil.
2.便利构造函数可调用self.init()函数.
3.便利构造函数不能被重写或者super调用.
便利构造函数的优势
可以定义便利构造器来调用同一个类中的自定义构造器,并为其参数提供默认值。可以使用便利构造器来创建一个特殊用途的实例。
便利构造器实例
//实例1:特定用途的构造函数(限制了age的范围)
convenience init?(name: String, age: Int) {
if age < 20 || age > 100 {
return nil
}
self.init(name:name age:age)
}
//实例2:类的继承 (缩减的输入参数)
class mainClass {
var num1 : Int // 属性
init(num1 : Int) {
self. num1 = num1
}
}
class subClass : mainClass {
var num2 : Int
init(num1 : Int, num2 : Int) {
num2 = num2
super.init(num1: num1)
}
// 便利构造函数只需要一个参数
override convenience init(num1: Int) {
self.init(num1: num1, num2:0)
}
}
let res = mainClass(num1: 20)
let res2 = subClass(num1: 30, num2: 50)
//使用便利构造函数,简化了参数。
便利构造函数应用场景
1.用给定参数来判断是否要创建对象,不需要像自定义构造函数那样,必须实例化一个对象。
2.在实际应用中,对已有类的构造函数进行扩展,可以使用便利构造函数,从而达到简化对象的创建的目的。
可失败构造器
如果一个类、结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器。
变量初始化失败可能的原因,一般有如下几种:
1.传入了无效的参数值而失败。
2.缺少某些的外部资源而失败。
3.没有满足特定条件的失败。
为了更好地处理这种构造函数可能会失败的情况。我们一般添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?) 或者(init!)。
实例:
class Student3 {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self. name = name
}
init!(name: String) {
if name.isEmpty { return nil }
self. name = name
}
}
let st4 = Student3(name: "Li lei")
let st5 = Student3(name: "")
//即当初始化Student3的时候,输入一个为空的name,就有可能初始化失败。
可失败构造器注意点:
1.通过在 init 关键字后面添加问号init?或者Init!(后者代表强制解包)。
2.可失败的构造函数里面应该有一个 return nil 的语句(没有也不报错)。
3.通过可失败的构造函数构造出来的实例是一个可选型,所以配合解包。
以上就是构造函数的相关知识。
下标
前面讲到了构造函数,这里再讲下标。
什么是下标
在Swift语言中,为了存储或获取数据的方便,针对类、结构体和枚举类型,创造了下标的概念。
通过使用下标的索引,可以快速地设置或者获取数据,而不需要再调用对应的存取方法了。举例来说,用下标访问Array实例中的元素时,可以写作array[index],访问Dictionary实例中的元素时,可以写作dictionary[key]。
下标语法
下标是使用subscript关键字来定义,类似函数,带有参数和返回值。其中参数和返回值分别作为入参和获取值。
subscript的内部实现:一个get块和一个可选的set块。get块用于返回值,set块用于设置值。
示例如下:
subscript(index: Int) -> Int {
get {
// 返回一个适当的Int类型的值
}
set(newValue) {
// 执行适当的赋值操作
}
}
其中newValue是一个隐藏的参数,其类型和返回类型相同。如同计算型属性,可以不指定setter的参数(newValue)。如果不指定参数,setter 会提供一个名为 newValue 的默认参数。
对于只读下标的声明,可以通过省略 get 关键字和对应的大括号组来进行简写:
subscript(index: Int) -> Int {
//返回一个适当的 Int 类型的值
}
小小结:
1.使用subscript关键字定义下标。
2.下标可以接受多个参数且类型任意。
3.下标支持返回类型,且类型任意。
4.下标可以没有set方法,但必须要有get方法。如果只有get方法,可以省略get。
5.下标有个默认参数(newValue),在不指定参数时提供。
6.下标的参数不能设置默认值,也不能设置为in-out类型。
下标实例
//实例1:获取下标值
class AddTable {
let adder: Int
subscript(index: Int) -> Int {
return adder + index
}
}
let addTable = AddTable(adder: 10)
print("after adder:\(addTable[3])")
// 打印“after adder:13”
在上例中,创建了一个AddTable实例,在输入下标索引之后,将在索引基础上再加上10,并把结果返回。这里演示获取下标数据的方式。
实例2:快速设置值
下标通常作为访问集合,列表或序列中元素的快捷方式。可以针对特定的类或结构体功能,用最恰当的方式实现下标。
例如,Swift的Dictionary类型实现下标用于对实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
var legs = ["Cat": 2, "Dog": 3, "Bird": 4]
legs["Ant"] = 2
上例定义一个名为 legs 的变量,并用一个包含三对键值的字典字面量初始化它。legs 字典的类型被推断为 [String: Int]。字典创建完成后,该例子通过下标将 String 类型的键 Ant 和 Int 类型的值2添加到字典中。
类型下标
前面讲的都是实例的下标,实例下标是在特定类型的一个实例上调用的下标。我们还可以定义一种在类型自身上的下标。这种下标被称作类型下标。一般通过在subscript关键字之前写下static关键字的方式来表示一个类型下标。下面的例子展示了如何定义和调用一个类型下标:
enum Color: Int {
case mercury = red, green, blue, black, yellow
static subscript(n: Int) -> Color {
return Color(value: n)!
}
}
let black = Color[3] //注:下标是从0开始
print(black) //black
注意:类类型可以使用class关键字来代替 static,它允许子类重写父类中对下标的实现。
至此,Swift入门基础,下标也介绍完了,下面介绍属性的概念。
计算属性
前面讲到了构造函数和下标。在Swift开发过程中,构造函数和下标的使用非常实用,当然还有一个也很实用的就是属性。
属性与成员变量的区别
首先先说下属性与成员变量的区别。属性具有set、get方法,是允许其他对象访问属性的。而成员变量恰恰相反,成员变量主要用于类内部,不与外界接触,其具有一定的私有性。
属性与成员变量的内部机制分别是:属性是公开的(public),其在创建过程中自动生成set和get方法。外界可以通过set或get方法访问;而成员变量是私有的(private),且不会生成set、get方法,外界无法访问。
在Swift中,属性分为存储属性、计算属性、懒加载属性、类属性。下面分别介绍
存储型属性
存储型属性(sorted variable):它具有属性的特性(有get或set方法),但是它还有个新特性:能存储一个常量或变量。我们可以理解为可以进行赋值和取值的变量。
class car {
//成员变量
var name:String="b"
//存储属性
var name:String{
get{
return self.name
}
set{
self.name= newValue
}
}
计算属性
计算型属性(computed variable):仅有get(readOnly语义)或有get+set的属性。当有get+set的属性,也只是作为其他属性的外部接口,供外部调用。
我们可以从字面上理解计算型的属性,这种属性没法存储值。设计出计算型属性只是为了调用其他属性而包装的读取方法,本身没有内存空间,也就不能赋值。
举例说明
//实例1
var title: String {
get {
return "所选学课:" + "\(name)"
}
}
//实例2:get+set正确的使用方法:作为其他属性的外部接口:
private var _squre: Double = 0.0
var squre: Double {
get {
return _squre
}
set {
if (newValue <= 0) {
print("newValue = \(newValue)")
} else {
_squre = newValue
}
}
}
小小结:
1.存储型属性,能存储数据。方便存储
2.计算型属性,不分配独立的存储空间,不能存储数据,只提供get和set方法。
懒加载属性
懒加载属性,是在属性前加上lazy关键字,起到延迟加载的作用。
还有其他的特性:
1.必须有默认值
2.在第一次访问的时候才被赋值
3.有线程安全隐患。
类型属性
1.使用static修饰,并且是全局变量
2.必须有值
3.类型只会被初始化一次
//举例:
static var default: Person = Person()
以上就是属性的介绍。文章来源:https://www.toymoban.com/news/detail-797002.html
到这里,关于构造函数,下标,属性的概念都介绍了。文章来源地址https://www.toymoban.com/news/detail-797002.html
到了这里,关于Swift入门基础:构造函数、下标、计算属性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!