Swift2.0语言教程之类的属性
Swift2.0语言教程之类的属性
类
虽然函数可以简化代码,但是当一个程序中出现成百上千的函数和变量时,代码还是会显得很混乱。为此,人们又引入了新的类型——类。它是人们构建代码所用的一种通用、灵活的构造方式。本章将主要详细讲解类的使用。
Swift2.0语言的类与对象
类是一种新的数据类型,类似于生活中犬类、猫类等等。而对象则是将这个抽象的类进行了具体化。例如,在犬类中,有哈士奇,金毛等等,这些就是犬类的具体化,即对象。本节将讲解类的创建以及如何将类进行具体化(即实例化)为对象。
Swift2.0语言中类的组成
在一个类中通常可以包含如图8.1所示的内容。
图8.1 类的构成
其中,这些内容的功能如下:
- q 属性:它将值和特定的类关联。
- q 下标脚本:访问对象、集合等的快捷方式。
- q 方法:实现某一特定的功能,类似于函数。
Swift2.0语言中创建类
在Swift中类的创建要比在Objective-C中简单的多。在Objecttive-C中,需要使用需要@interface和@end对类中的内容进行声明,还需要使用@implementation和@end对类声明的内容进行实现。在Xcode 6.3之前,它们需要放置在不同的文件中。虽然在Xcode 6.4中,它们可以放置在一个文件中,但是也相当的麻烦。Swift语言推出了自己创建类的方式,只使用一个class关键字,其一般的创建形式如下:
- class 类名{
- //具体内容
- }
注意:在类中可以定义属性和方法,这些内容会在后面做详细的介绍。类名可以使用“大骆驼拼写法”方式来命名(如SomeClass),以便符合标准Swift 类型的大写命名风格(如String,Int和Bool)。对于后面所讲的对象、属性以及方法等可以使用“小骆驼拼写法”来命名。
【示例8-1】以下创建一个名为NewClass的类。代码如下:
- class NewClass{
- }
该类名称为NewClass。由于其中没有属性和方法,所以它只是一个空类。
Swift2.0语言实例化对象
实例化对象也可以称为类的实例,其语法形式如下:
- var/let 对象名=类名()
【示例8-2】以下会创建一个类名为NewClass的类,然后再进行实例化。代码如下:
- import Foundation
- class NewClass{
- }
- let newClass=NewClass ()
注意:在进行实例化时,类名后一定要加上()。否则程序就会错误,如以下的代码:
- var newClass = NewClass
由于在实例化时缺少了(),导致程序出现以下的错误信息:
- Expected member name or constructor call after type name
以上所讲的这些只是简单的实例化对象。它使用了最简单的构造器来生成一个对象。在后面的章节中我们会为开发者讲解构造器的具体用法。
属性
在Objective-C中,属性是使用关键字@property关键字进行声明的内容。在Swift中,属性可以将值跟特定的类、结构或枚举关联。属性一般分为存储属性、计算属性和类型属性。本节将讲解对这些属性做详细的讲解。
Swift2.0语言存储属性
存储属性就是存储特定类中的一个常量或者变量。根据数据是否可变,分为常量存储属性和变量存储属性。
1.定义存储属性
常量存储属性使用let关键字定义(声明定义在一起进行,为了方便称为定义),其语法形式如下:
- let 常量存储属性名:数据类型=初始值
变量存储属性可以使用var关键字定义,其语法形式如下:
- var 变量存储属性名:数据类型=初始值
【示例8-3】以下代码定义类NewClass1,其中包含两个属性value1和value2,代码如下:
- class NewClass1 {
- let value1=20
- var value2:Int=10
- }
其中,value1使用let定义为常量存储属性,value2使用var定义为变量存储属性。在定义存储属性时,初始值是必不可少的,否则,就会出现错误。例如,以下的代码:
- class NewClass1 {
- let value1=20
- var value2:Int
- }
在此代码中,由于value2后面未加初始值,导致程序出现以下的错误信息:
- Class 'NewClass1' has no initializers
2.访问存储属性
对于这些存储属性的访问,需要使用“.”点运算符。其语法形式如下:
- 对象名.常量存储属性名/变量存储属性名
【示例8-4】以下定义了3个存储属性firstValue、secondValue、thirdValue,然后进行访问。代码如下:
- import Foundation
- class NewClass{
- let firstValue:Int = 0
- let secondValue=200
- var thirdValue:String="Hello"
- }
- let newclass=NewClass()
- //存储属性的访问
- print("firstValue=\(newclass.firstValue)")
- print("secondValue=\(newclass.secondValue)")
- print("thirdValue=\(newclass.thirdValue)")
运行结果如下所示:
- firstValue=0
- secondValue=200
- thirdValue=Hello
注意:对存储属性进行访问时,只可以对在自己类中定义的存储属性进行访问,否则就会出现错误,代码如下:
- import Foundation
- class NewClass1 {
- var class1Value=10
- }
- class NewClass2 {
- var class2Value=10
- }
- let newclass1=NewClass1()
- print(newclass1.class1Value)
- print(newclass1.class2Value)
在此代码中,由于class2Value存储属性是在NewClass2类中定义的,而不是NewClass1中定义的,所以程序就会出现以下的错误信息:
- 'NewClass1' does not have a member named 'class2Value'
存储属性除了可以使用“.”点运算符进行读取外,还可以对其进行修改。修存储改属性的一般语法形式如下:
- 对象名.存储属性=修改的内容
【示例8-5】以下代码就将secondValue的属性值"Hello"修改为了"Swift",代码如下:
- import Foundation
- class NewClass{
- var secondValue:String="Hello"
- }
- let newclass=NewClass()
- print("修改前:secondValue=\(newclass.secondValue)")
- newclass.secondValue="Swift" //修改存储实现
- print("修改后:secondValue=\(newclass.secondValue)")
运行结果如下所示:
- 修改前:secondValue=Hello
- 修改后:secondValue=Swift
注意:只有变量存储属性才可以进行属性修改,常量存储属性不可以进行属性修改。如以下的代码:
- import Foundation
- class NewClass{
- let firstValue:Int = 0
- }
- let newclass=NewClass()
- print("修改前:firstValue=\(newclass.firstValue)")
- newclass.firstValue=100 //试图对属性firstValue的值进行修改
- print("修改后:\(newclass.firstValue)")
由于在类中使用了let对存储属性进行了定义,其值是不可以进行修改的,所以出现了以下的错误信息:
- annot assign to 'let' property 'firstValue'
3.延迟存储属性
如果开发者只有在第一次调用存储属性时才能确定初始值,这时需要使用延迟存储属性实现。它的定义一般需要使用关键字lazy实现的,其语法形式如下:
- lazy var 属性名:数据类型=初始内容
注意:在延迟存储属性中初始内容是不可以省去的。数据类型也是可以省去的,因为swift会根据初始内容自行判断数据类型。
【示例8-6】以下将使用lazy来定义一个延迟存储属性importer,代码如下:
- import Foundation
- class DataImporter {
- var fileName = 123456
- }
- class DataManager {
- lazy var importer = DataImporter()
- var data = [String]()
- }
- let manager = DataManager()
- manager.data += ["Some more data"]
- print(manager.data)
- print(manager.importer.fileName)
在没有调用manager.importer.fileName时,实例的 importer属性还没有被创建。运行结果如下所示:
- [Some more data]
- 123456
我们可以使用断点调试的方法对此代码进行调试,来查看它的运行结果。具体步骤如下:
(1)为几行关键代码添加断点,并再添加一行代码,来查看添加importer的值,如图8.2所示。
图8.2 添加断点
(2)单击运行按钮,此时会在第一个断点处出现一个蓝色的箭头,表示此行代码在运行。然后选择Debug|Continue命令,按下调试窗口工具栏中的Continue program execution按钮,查看程序的执行,其中,可以使用查看器来观察属性值的变化。属性查看器位于调试信息窗口左半部分,如图8.3所示。程序的执行,如图8.4所示。其中,看到的self、data、importer.storage(因为importer是延迟属性,为了和其他属性区分,所以在查看器上看到是importer.storage)等都是属性。它们会随程序的执行为改变。
图8.3 查看器位置
图8.4 调试
在此图中程序执行到第3步,也就是第三个图时,类DataManager的属性data初始化,但没有给importer.storage属性进行初始化,一直为nil,直到执行到第7步,即第7个图时,才为importer.storage属性的属性初始化。
在定义一个延迟存储属性时需要注意以下2点,
(1)定义一个延迟存储属性时除了lazy外,还需要使用var关键字,但是不能使用let关键字,否则程序就会出现错误,如以下代码:
- class DataImporter {
- var fileName = 123456
- }
- class DataManager {
- lazy let importer = DataImporter()
- var data = [String]()
- }
由于在定义延迟属性时使用了let关键字,所以导致程序出现了以下的错误:
- 'lazy' cannot be used on a let
(2)初始内容是不可以省去的,否则程序就会出现错误,如以下的代码,定义了一个没有初始值的延迟属性。代码如下:
- class DataManager {
- lazy var value:Int
- }
由于在此代码中没有为value指定初始值,导致程序出现了以下的错误:
- lazy properties must have an initializer
计算属性
除了存储属性外,类中还可以定义计算属性。计算属性不存储值,而是提供了一个getter和setter来分别进行获取值和设置其他属性的值。getter使用get关键字进行定义,其一般形式如下:
- get{
- …
- return 某一属性值
- }
- setter使用set关键字进行定义,其一般语法形式如下:
- set(参数名称){
- …
- 属性值=某一个值
- …
- }
当然,它也可以没有参数名称。这种情况会后面的内容中讲解。在计算属性中同时包含了getter和setter,其一般定义形式如下:
- var 属性名:数据类型{
- get{
- …
- return 某一属性值
- }
- set(参数名称){
- …
- 属性值=某一个值
- …
- }
- }
【示例8-7】以下代码定义了一个类WalletClass,用来保存钱包中的金额,其默认单位为美元。为了方便用户以人民币为单位进行访问值和设置值,所以使用了计算属性cal。代码如下:
- import Foundation
- class WalletClass{
- var money=0.0
- var cal:Double{ //定义计算属性cal
- get{ //定义getter
- let RMB=money*6.1
- return RMB //返回以人民币为单位的金额
- }
- set(RMB){ //定义setter
- money=RMB/6.1 //返回以美元为单位的金额
- }
- }
- }
- var mywallet=WalletClass()
- mywallet.cal=(20)
- //输出
- print(mywallet.cal)
- print(mywallet.money)
运行结果如下所示:
- 20.0
- 3.27868852459016
注意:在使用计算属性时需要注意以下三点:
1.定义计算属性的关键字
在定义一个计算属性时,必须且只能使用var关键字,否则就会出现错误。以下的代码,将示例8-7的代码做了一些修改,代码如下:
- class WalletClass{
- var money=0.0
- let cal:Double{
- get{
- var RMB=money*6.1
- return RMB
- }
- set(RMB){
- money=RMB/6.1
- }
- }
- }
在此代码中定义一个计算属性,但是使用了let关键字,导致程序出现了以下的错误:
- 'let' declarations cannot be a computed property
2.数据类型
在定义计算属性时,一定要为属性指定一个明确的数据类型,否则就会出现错误提示。例如以下的代码,是将示例8-7中的代码做了一些修改。代码如下:
- class WalletClass{
- var money=0.0
- var cal{ //没有设置cal的数据类型
- get{
- var RMB=money*6.1
- return RMB
- }
- set(RMB){
- money=RMB/6.1
- }
- }
- }
在此代码中由于没有为计算属性指定一个明确的数据类型,导致程序出现了以下的错误信息:
- Computed property must have an explicit type
- Type annotation missing in pattern
3.set后面的参数类型
在使用计算属性时,set后面的参数类型要和返回值的类型相同,不需要再指定类型。否则,程序就会出现错误。如以下的代码,此代码是将示例8-7中的代码做了一下修改,代码如下:
- class WalletClass{
- var money=0.0
- var cal:Double{ //没有设置cal的数据类型
- get{
- var RMB=money*6.1
- return RMB
- }
- set(RMB:String){ //为参数定义了数据类型
- money=RMB/6.1
- }
- }
- }
在此代码中,对set后面的参数RMB指定了数据类型,导致程序出现了以下的错误:
- Expected ')' after setter value name
- Expected '{' to start setter definition
4.没有定义参数名称
如果计算属性的setter没有定义表示新值的参数名,则可以使用默认名称newValue。
【示例8-8】以下代码就使用了newValue来实现了华氏温度和摄氏温度的转换。代码如下:
- import Foundation
- class DegreeClass{
- var degree=0.0
- var cal :Double{
- get{
- let centigradedegree=(degree-32)/1.8
- return centigradedegree
- }
- set{
- degree=1.8*newValue+32 //没有定义参数名称,可以使用默认的
- }
- }
- }
- var degreeClass=DegreeClass()
- degreeClass.cal=(10.0)
- print(degreeClass.cal)
- print(degreeClass.degree)
运行结果如下所示:
- 10.0
- 50.0
4.定义参数名后不能使用默认参数名
在set后面如果定义了参数名称,就不能再使用Swift默认的参数名称newValue。否则,就会导致程序出现错误。如以下的代码,将示例8-8做了一些修改,代码如下:
- import Foundation
- class DegreeClass{
- var degree=0.0
- var cal :Double{
- get{
- let centigradedegree=(degree-32)/1.8
- return centigradedegree
- }
- set(aaa){ //定义参数名称后,使用默认参数
- degree=1.8*newValue+32
- }
- }
- }
- …
在此代码中,set后面定义了参数名称,但是又使用了默认的参数名称,导致程序出现了以下的错误:
- Use of unresolved identifier 'newValue'
5.setter和getter的省略
在计算属性中,如果只有一个getter,则称为只读计算属性。只读计算属性可以返回一个值,但不能设置新的值。
【示例8-9】以下将通过getter来获取名称的字符串。代码如下:
- import Foundation
- class PersonName{
- var name:String=""
- var returnName :String{
- if (name.isEmpty) {
- return "NULL"
- }else{
- return name
- }
- }
- }
- var personClass=PersonName()
- print("没有名字时\(personClass.returnName)")
- personClass.name=("Tom")
- print("有名字时\(personClass.returnName)")
在此代码中,当刚创建实例PersonName后,就去访问returnName。由于name默认为空,所以会返回字符串"NULL"。再对name赋值后,再一次访问returnName。由于name不为空,所以会返回name的内容。运行结果如下所示:
- 没有名字时NULL
- 有名字时Tom
注意:1.在只读计算属性中,可以将get关键字和花括号去掉。2.在C#等其他语言中可以将属性分为只读属性(只有getter)、只写属性(只有setter)和可读可写属性。但是在Swift中就不同了,只有只读计算属性和可读可写计算属性两个。没有只写计算属性,否则程序就会出现错误,如以下的代码:
- class PersonName{
- var name:String=""
- var setName :String{
- set{
- …
- }
- }
在此代码中定义了一个只写计算属性,导致程序出现了以下的错误:
- Variable with a setter must also have a getter
Swift2.0语言的类型属性
类型属性就是不需要对类进行实例化就可以使用的属性。它需要使用关键字class进行定义,其定义形式如下:
- class var 类型属性名:数据类型{
- …
- 返回一个值
- }
例如下面代码定义了一个类型属性count,代码如下:
- class var count:Int{
- return 20
- }
类型属性也是可以被访问的,其访问类型属性的一般形式如下:
- 类名.类型属性
【示例8-10】以下代码定义了一个类型属性newvalue,然后进行遍历访问该属性的每一个字符,并输出。代码如下:
- import Foundation
- class NewClass {
- class var newvalue:String{ //定义类型属性newvalue
- return "Hello"
- }
- }
- print(NewClass.newvalue)
- print("遍历NewClass.newvalue:")
- //遍历类型属性newvalue的值
- for index in NewClass.newvalue.characters {
- print(index)
- }
- 运行结果如下所示:
- Hello
- 遍历NewClass.newvalue:
- H
- e
- l
- l
- o
在使用类型属性时需要注意以下2点:
1.let关键字不能声明类型属性
定义类型属性时除了有关键字class外,还需要使用var关键字,但不能使用let关键字,否则程序提示错误,如以下代码,此代码定义了一个类型属性newvalue。代码如下:
- import Foundation
- class NewClass {
- class let newvalue:Int{
- return 20
- }
- }
- print(NewClass.newvalue)
在此代码中使用了关键字let进行了类型属性的声明,导致程序出现了以下的错误:
- 'let' declarations cannot be computed properties
2.存储属性
在类型方法中不能使用存储属性,否则程序就会出现错误,如以下的代码,此代码实现的是输出字符串"Hello"。
- import Foundation
- class NewClass {
- var count:Int=20
- class var newvalue:Int{
- return count
- }
- }
- print(NewClass.newvalue)
其中,count是存储属性,newvalue是类型属性。在代码中,将str用在newvalue中,导致程序出现了以下的错误:
- 'NewClass.Type' does not have a member named 'count'
3.对象不能访问类型属性
类型属性只可以使用类去访问,而不可以使用对象进行访问。否则,就会出现错误,如以下的代码:
- import Foundation
- class NewClass {
- class var newvalue:Int{
- return 20
- }
- }
- var newClass=NewClass()
- print(newClass.newvalue)
在此代码中,定义了一个类型属性newvalue,但在访问它时使用了对象,导致程序出现了以下错误:
- 'NewClass' does not have a member named 'newvalue'
类型属性和存储属性一样,除了可以进行访问外,还可以进行修改,其语法形式如下:
- 类名.类型属性=修改的内容
【示例8-11】以下程序将类型属性0变为200,并输出。代码如下所示:
- import Foundation
- var value:Int=0
- class NewClass{
- class var count :Int{
- get{
- let newvalue=value
- return newvalue
- }
- set{
- value=newValue
- }
- }
- }
- print("修改前:\(NewClass.count)")
- NewClass.count=200
- print("修改后:\(NewClass.count)")
运行结果如下所示:
- 修改前:0
- 修改后:200
Swift2.0语言中的属性监视器
属性监视器用来监控和响应属性值的变化。每次属性被设置值的时候,都会调用属性监视器,哪怕是新的值和原先的值相同。一个属性监视器由willSet和didSet组成,其定义形式如下:
- var 属性名:数据类型=初始值{
- willSet(参数名){
- …
- }
- didSet(参数名){
- …
- }
- }
其中,willSet在设置新的值之前被调用,它会将新的属性值作为固定参数传入。didSet在新的值被设置之后被调用,会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。
【示例8-12】以下将使用属性监视器监视totalSteps属性值的变化。代码如下:
- import Foundation
- class StepCounter {
- var totalSteps: Int = 0 {
- //完整的属性监视器
- willSet(newTotalSteps) {
- print("新的值为 \(newTotalSteps)")
- }
- didSet(old) {
- if totalSteps > old {
- print("与原来相比增减了 \(totalSteps - old) 个值")
- }
- }
- }
- }
- let stepCounter = StepCounter()
- stepCounter.totalSteps = 0
- stepCounter.totalSteps = 200
- stepCounter.totalSteps = 400
- stepCounter.totalSteps = 800
运行结果如下所示:
- 新的值为 0
- 新的值为 200
- 与原来相比增减了 200 个值
- 新的值为 400
- 与原来相比增减了 200 个值
- 新的值为 800
- 与原来相比增减了 400 个值
注意:在使用属性监视器时,需要使用注意以下4点:
1.不指定参数名
在willSet后面是可以不指定参数的,这时Swift会使用默认newValue表示新值。例如以下的代码在没有指定willSet参数的情况下,直接使用newValue来输出新的值。代码如下:
- import Foundation
- class StepCounter {
- var totalSteps: Int=0 {
- willSet {
- print("新的值为 \(newValue)")
- }
- didSet (old){
- if totalSteps > old {
- print("与原来相比增减了 \(totalSteps - old) 个值")
- }
- }
- }
- }
- let stepCounter = StepCounter()
- stepCounter.totalSteps = 0
- stepCounter.totalSteps = 200
运行结果如下所示:
- 新的值为 0
- 新的值为 200
- 与原来相比增减了 200 个值
同样在didSet后面也可以不指定参数名,此时Swift会使用默认参数名oldValue。如以下的代码,此是示例8-12的代码做了一个修改,代码如下:
- import Foundation
- class StepCounter {
- var totalSteps: Int = 0 {
- //完整的属性监视器
- willSet(newTotalSteps) {
- print("新的值为 \(newTotalSteps)")
- }
- didSet {
- if totalSteps > oldValue {
- print("与原来相比增减了 \(totalSteps - oldValue) 个值")
- }
- }
- }
- }
- let stepCounter = StepCounter()
- stepCounter.totalSteps = 0
- …
2.默认参数不可以交换使用
在使用willSet和didSet时,它们默认的参数可以是不可以交换使用的。例如在willSet中使用的newValue不可以使用在didSet中,在didSet中使用的oldValue不可以使用在willSet中,否则程序就会出现错误。例如以下的代码,将示例8-12做了一些修改,代码如下:
- import Foundation
- class StepCounter {
- var totalSteps: Int = 0 {
- //完整的属性监视器
- willSet {
- print("新的值为 \(newValue)")
- }
- didSet {
- if newValue > oldValue {
- print("与原来相比增减了 \(newValue - oldValue) 个值") //输出新值和旧值之间的差值
- }
- }
- }
- }
- let stepCounter = StepCounter()
- …
在此代码中,由于在didSet中使用了willSet中的默认参数,导致程序出现了以下的错误:
- Use of unresolved identifier 'newValue'
3.延迟属性不能使用属性监视器
在延迟属性中不可以使用属性监视器,否则程序会出现错误。如以下的代码:
- class StepCounter {
- lazy var totalSteps: Int=0 {
- willSet {
- print("新的值为 \(newValue)")
- }
- didSet {
- if totalSteps > oldValue {
- print("与原来相比增减了 \(totalSteps - oldValue) 个值")
- }
- }
- }
- }
此代码中延迟属性中添加了属性监视器,导致程序出现了如下的错误:
- lazy properties may not have observers
4.分开使用willSet和didSet
一个完整的属性监视器由willSet和didSet组成,但是willSet和didSet也可以单独使用。例如以下的代码就只使用了willSet输出了新值的信息。代码如下:
- import Foundation
- class StepCounter {
- var totalSteps: Int=0 {
- willSet {
- print("新的值为 \(newValue)")
- }
- }
- }
- let stepCounter = StepCounter()
- stepCounter.totalSteps = 0
- stepCounter.totalSteps = 200
- stepCounter.totalSteps = 600
- stepCounter.totalSteps = 1200
这里属性监视器total只使用了willset,而没有使用didset。运作结果如下所示:
- 新的值为 0
- 新的值为 200
- 新的值为 600
- 新的值为 1200
本文选自:Swift2.0语言快速入门v3.0 大学霸内部资料,转载请注明出处,尊重技术尊重IT人!