[Swift]类(Class)——属性

来源:互联网 发布:vr设备 知乎 编辑:程序博客网 时间:2024/05/20 15:42

在Swift语言中,提供了全面的面向对象的支持。首先支持类定义,类中可以定义各种类型的方法和属性,可以通过设置访问修饰符来控制哪些成员和方法是对外界可见的,哪些是不可见的,实现对类的封装。

同时也可以继承一个类或者多个类,实现类的继承性。还有一个叫协议的概念,等同于别的编程语言中的接口。协议与类有相同也有不同。

  • 相同之处:都是可以继承扩展的,协议本身也可以继承别的协议。
  • 不同之处:协议不支持属性,方法也只能声明,不实现。协议独立来看,也不能实例化成某个对象。也就是说,协议需要某个具体的类去继承,这叫实现协议。实现协议之后,就叫某个类遵守或者符合某个协议。

一、编写第一个类

类是某一类事物的抽象,通过抽象定义了一件事物的特点,通常来说,类定义了事物的属性和行为。从程序上讲,类时生成将来所要用的具体对象的代码模板。

class Person {    //类型属性、方法}/*在Swift语言中,Person类的声明和其他语言一样,以关键字class声明,类的名称首字母一般建议大写,它可以是任意数字、字母、下划线组合,不过不能以数字开头。*/

尽管其主体是空的,没有任何实际的用处,我们已经完成了类的最简单的定义,定义一个类,定义了一种新的数据类型,这种类我们叫做空类,空类也是可以被调用的。

var p = Person()println(p)

上面代码中,P就是对象,是Person类的具体化,类是对象的模板,定义一个类,可以生成无数个对象。生成对象的过程叫类的实例化

1、属性

属性就是类所表示的现实对象的特性在代码中的反映,比如现实中有学生这个身份类别,学长的属性有姓名、班级、籍贯,把相应的内容对应到类代码即可:

class Student{    var name:String = ""           var classno:Int = 0    var from:String = ""}
class Student{    var name:String = ""        //变量属性    var classno:Int = 0    var from:String = ""    let country:String = "中国" //常量属性}var student = Student()student.name = "HTX"student.classno = 1student.from = "NanTong"println("\(student.name) \(student.classno) \(student.from)")//输出:HTX 1 NanTong

在Swift语言中,属性的类型可以分为两种:存储属性和计算属性

存储属性就是用来表示类的一个特性。计算数学本身并不直接存储特性,提供的是一个计算后的结果。

1.1、存储属性

正如变量一样,属性也分为变量属性和常量属性,都叫存储属性,并且也分别用关键字var 和关键字let来描述。
上例中得name、classno、from就是变量属性,而country则是常量属性。

以下三个注意事项:
(1)存储属性在定义时,需要为每一个属性定义一个默认值,当然也可以在初始化的时候设置属性的初始值。如果初始化也不定义默认值,在调用super.init()时会出错,或者在声明了对象之后,也无法为此变量赋值,除非明确定义为强制解包可选。
强制解包可选:

class StudentOptional{    var name:String!    var classno:Int!    var from:String!    let country:String!}var studentOptional = StudentOptional()studentOptional.name = "HTX"studentOptional.classno = 2studentOptional.from = "RuGao"

(2)还有一个需要注意的地方就是存储属性中的常量属性,常量属性并不意味着绝对不能变化,而是要看情况,如果存储属性的常量属性是值类型,比如字符串、结构体,就不能再变,结构体的属性也不能再变。如果是类,那么就可能重新赋值。

class Person{    var name:String!}class StudentOptional{    var name:String!    var classno:Int!    var from:String!    let country:String = "china"    let friend:Person = Person()}var studentOptional = StudentOptional()studentOptional.name = "HTX"studentOptional.classno = 2studentOptional.from = "RuGao"//下面这行会出错//studentOptional.country = "中国"//下面这一行就不会出错studentOptional.friend.name = "HeTianXiong"

(3)之前说到,存储属性一般情况下要求初始化,我们可以设置一个lazy修饰符,使得这种初始化延迟,尽管我们在属性声明时已经做了这种初始化上的定义。所谓修饰符就是加缀在变量、函数、类等得定义前面,用来约束或者增强变量、函数、类功能的一种特殊关键字。

lazy修饰的存储属性,叫懒惰储存属性。懒惰存储属性是当它第一次被使用时才进行初值计算。比如当属性初始值因为外部原因在实例初始化完成之前不能确定时,就要定义成懒惰存储属性。当属性初始值需要复杂或高代价的设置,在它需要时才被赋值时,懒惰属性就派上用场了。

class Deposit{    init(){        println("init at deposit")    }}class PersonLazy {    var name:String!    lazy var money:Deposit = Deposit()}class StudentLazy{    var name:String!    var classno:Int!    var from:String!    let country:String = "china"    let friend:PersonLazy = PersonLazy()}var studentlazy = StudentLazy()studentlazy.name = "HTX"studentlazy.classno = 1studentlazy.from = "suzhou"studentlazy.friend.name = "huhu"

此类定义了一个lazy属性,所以”init at deposit”根本不会输出,因为在本例中没有用到,就没有赋值计算,如果没有lazy的修饰,这句话就会在上面的代码所有输出之前中输出。

懒惰属性必须时变量属性,这个很容易理解,因为它的初始值直到实例初始化完成后,在调用时才被计算,常量属性在实例初始化完成之前就应该被赋值,因此常量属性不能被声明为懒惰属性。

1.2、计算属性

计算属性在别的编程语言中一般以成员函数或者方法的形式出现的。当然在Swift中也可以使用方法,不过提供一种更为灵活的选择。
计算属性并不能直接地保存数据,但是可以用getter和setter来间接地获得或者改变其他属性和值

首先我们做一个简单的计算器:
1、用方法来实现

class Calculator {    var a:Int = 1    var b:Int = 1    init(a:Int,b:Int){        self.a = a        self.b = b    }    func sum()->Int {        return a + b    }    func quotient()->Int{        return a/b    }    func product()->Int{        return a * b    }    func difference()->Int {        return a - b    }}

2、使用计算方式实现:

class PCalculator {    var a:Int = 1    var b:Int = 1    var sum:Int{        return a + b    }    var difference:Int{        return a - b    }    var product:Int{        return a * b    }    var quotient:Int {        return a/b    }    init(a:Int,b:Int){        self.a = a        self.b = b    }   }

虽然计算属性并不直接的保存变量值,但是可以通过对其值得设置来改变别的属性值,只有比较方便,在前面的实例基础上加一个scale计算属性,scale本身无须存储,它是用于等比扩大两个值的。当然其他计算属性的值也要跟着变化。

class PCalculator {    var a:Int = 1    var b:Int = 1    var sum:Int{        return a + b    }    var difference:Int{        return a - b    }    var product:Int{        return a * b    }    var quotient:Int {        return a/b    }    init(a:Int,b:Int){        self.a = a        self.b = b    }    var scale:Int{        get{            return a/a        }        set{            a = a * newValue            b = b * newValue        }    }}var pcal = PCalculator(a: 30, b: 10)pcal.scale = 3println("\(pcal.sum)    \(pcal.quotient)    \(pcal.product)   \(pcal.difference)")//输出:120 3  2700  60

有关计算属性,有两个注意事项:

  • 1、计算属性(包含只读计算属性)都应该使用var关键字,因为他们的值并不是固定的。let关键字只被常量属性使用,若用let定义计算属性,编译器会报错。
  • 2、使用计算属性一定要声明类型,否则报错。

1.3、属性观察者

属性观察者,个人理解更像是触发器,在数据库中就有触发器的概念,当一个操作发生时,触发另一个预置好的操作,也有点像回调函数,当某一个事件触发之前或者之后调用。

不过属性观察者同触发器不同的是,还可以在属性变更之前触发!属性观察者观察属性值的改变并对此作出响应。当设置属性的值时,属性观察者就被调用,即使当新值同原值相同时也会被调用。除了懒惰存储属性,你可以为任何存储属性加上属性观察者定义。另外,通过重写子类型属性,也可以继承属性(存储或计算)加上属性观察者定义。

在2048游戏中,就用到了属性观察者定义,具体是在积分面板上,玩游戏时,变更的是分数,但是积分面板不仅仅有分数,还有其他文字,甚至有两种积分——普通分和最高分,每一次变更分数时,面板的数字也会跟着改变。

var label:UILabel!var stype:String! //显示最高分还是分数var score:Int = 0{    didSet{        //分数变化,标签内容也变化        label.text = "\(stype):\(score)"    }}

属性观察者也是过去类行为的优化。在别的语言中,上面代码可能如下所示:

var label:UILabel!var stype:String! //显示最高分还是分数var score:Int = 0func setScore(score:Int){    didSet{        self.score = score        //分数变化,标签内容也变化        label.text = "\(self.stype):\(self.score)"    }}

上述代码专门为score设计了 setter函数,在每一次赋值的时候,关联更新标签上的文本,这样带来不好的地方,将属性更新和其他界面表现操作耦合在一起,可见使用属性观察者更加简洁。

对于属性观察者,有如下需要注意点:

  • 1、给属性添加观察者必须要声明清楚属性类型,否则编译器报错
  • 2、willSet可以带一个newName的参数,如果不这样的话,正如计算属性中医院买这个参数就被默认位newValue。
  • 3、didSet也可以待一个oldName的参数,表示旧的属性,如果不带,这个参数就被默认命名为oldValue。
  • 4、属性初始化时,willSet和didSet并不会调用,只有在初始化上下文之外,当设置属性时才被调用
  • 5、willSer和didSet每次设置属性值都会被调用,即使设置的值与原来的相同。
var stype:String! //显示最高分还是分数var score:Int = 0{    willSet{        //分数变化,标签内容也变化        label.text = "\(stype):\(newValue)"    }}

1.4、类型属性

实例属性是特定类实例的属性。当创建一个类实例时,这个实例有自己的属性值的集合,这将它与其他实例区分开,每一个类实例都有自己的存储空间,这些实例的属性也有自己专门的存储空间。

但是也可以定义一种属性,这种属性不属于任何一个类的实例,即不属于任何一个对象。即使创建再多这个类的实例,这个属性也不属于任何一个,它只属于类本身,这样的属性称为类型属性。类型属性在别的语言中叫做类静态变量

类型属性有点像全局变量,可以在定义后直接使用,不过全局变量不依赖于任何类而存在,类型属性则依赖于某一个类而存在,所以在调用时,需要增加类前缀,并受限于该类型属性在类的访问层级。

类型属性定义的方法很简单,我们在类中定义时,直接在变量关键字var前加上class关键字,同时要用get函数的设置的方法返回变量的值,而不能直接赋值。

class Teacher {    var name:String!    var classno:Int!    var from:String!    class var country:String{        return "中国"    }}println(Teacher.country)//输出:中国

===========================================================
篇幅略长,类的方法在下一篇文章。

0 0