Swift入门基础:构造函数、下标、计算属性

前文介绍了可选类型闭包,这两个概念在做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()

以上就是属性的介绍。

到这里,关于构造函数,下标,属性的概念都介绍了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员华仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值