属性:也叫类的成员变量。成员变量不能独立于类而存在。成员变量是描述类的对象的状态数据。
属性就是类所表示的现实对象的特性在代码中的反应。在 Swift 语言中属性的类型分为两种,存储属性和计算属性。
-
1)存储属性:用来表示类的一个特性的属性。正如变量和常量一样,属性也分为变量属性和常量属性,都叫存储属性。并且分别用关键字 var 和关键字 let 来描述。对于存储属性需要注意以下三点注意事项。
第一、存储属性在定义时,需要为每一个属性定义一个默认值,当然也可以在初始化的时候设置属性的初始值。如果初始化也不定义默认值,在调用 super.init() 时会出错,或者在声明了对象之后,也无法为此变量赋值,除非属性变量被明确定义为强制解包可选。
第二、存储属性中的常量属性并不意味着绝对不能变化,而是要看情况。如果存储属性的常量属性是值类型,比如字符串、结构体,就不能再变,结构体的属性也不能再变。如果是类,那么就可能重新赋值。
-
第三、存储属性一般要求初始化,我们可以设置一个 lazy 修饰符,使得这种初始化延迟,尽管我们在属性声明时已经做了这种初始化上的定义。
所谓修饰符就是加在变量、函数、类等的定义前面,用来约束或者增强变量、函数、类功能的一种特殊的关键字。比如修饰符 objc 表示导出给 OC 调用。
-
lazy 修饰的存储属性叫懒惰存储属性(懒加载)。懒惰存储属性是指当他第一次被使用时才进行初值计算。懒惰存储属性必须是变量属性(即用 var 定义的属性),因为他的初始值直到实例初始化完成之后,在调用之时才被计算。常量属性在实例初始化完成之前就应该被赋值,因此常量属性不能够被声明为懒惰存储属性。
lazy var shops:NSArray = {
let filePath:String = NSBundle.mainBundle().pathForResource("shops", ofType: "plist")!
let tmp = NSArray(contentsOfFile: filePath)!
return tmp
}()
在全局变量中不需要显式的指定 lazy 属性,而是所有的全局变量都是延迟计算的。
-
2)计算属性:本身并不直接存储特性,提供的是一个计算后的结果。计算属性并不能直接的保存数据,但是可以使用 getter 和可选的 setter 来间接的获得或者改变其他的属性和值。
var scale:Int {
get {
return a/b
}
set {
a = a * newValue
b = b * newValue
}
}
var scale:Int {
get {
return a/b
}
set (val) {
a = a * val
b = b * val
}
}
var sum:Int {
return a+b
}
var sum:Int {
get {
return a+b
}
}
-
3)属性观察者:属性观察者更像是触发器,不过属性观察者同触发器不同的是,还可以在属性变更前触发。属性观察者观察属性值的改变并对此作出响应。当设置属性的值时,属性观察者就被调用,即使当新值同原值相同时也会被调用。除了懒惰存储属性,你可以为任何存储属性加上属性观察者定义。另外,通过重写子类型属性,也可以为继承的属性(存储或计算)加上属性观察者定义。
var score:Int = 0 {
willSet {
label.text = "\(stype):\(newValue)"
}
}
var score:Int = 0 {
didSet {
label.text = "\(stype):\(score)"
}
}
-
属性观察者,有如下需要注意的几点:
1、给属性添加观察者必须要声明清楚属性类型,否则编译器报错。
2、willSet 可以带一个 newName 的参数,如果不这样的话,正如计算属性中一样,这个参数就默认的被命名成 newValue。
3、didSet 也可以带一个 oldName 的参数,表示旧的属性,如果不带,这个参数默认命名为 oldValue。
4、属性初始化时,willSet 和 didSet 并不会被调用。只有在初始化上下文时,当设置属性值时才被调用。
5、willSet 和 didSet 每次设置属性值都会被调用,即使是设置的值和原来的值相同。
-
4)类型属性:可以定义一种属性,这种属性不属于任何一个类的实例,即不属于任何一个对象。即使创建再多的这个类的实例,这个属性也不属于任何一个。它只属于类本身,这样的属性就称为类型属性。我们在类中定义的时候,直接在关键字 var 前加上 class 关键字,同时要用 get 函数的设置方法返回变量的值,而不能直接赋值,因为目前不支持。
class Teacher {
var name:String = ""
class var coumtry:String {
return "中国"
}
}
-
其实类型属性也可以改写,但是目前 Swift 语言还支持的不太好。
class Teacher {
var name:String = ""
class var country:String {
get { return "中国" }
// 这里会导致循环赋值,写入不成功
set { Teacher.country = (newValue) }
}
}
-
但可以使用一些折中的解决方案。
class Teacher {
var name:String = ""
// 使用带有静态属性的结构体
private struct scountry {static var country:String = "中国"}
public class var country:String {
get { return scountry.country }
set { scountry.country = newValue }
}
}
方法:另一个说法也叫成员函数,英文单词为 method,而不是表示函数的单词 function,所以叫方法。方法中定义的是类的行为。
-
1)实例方法:在类内部的定义和实现上看起来同函数没有区别,不过是能直接访问类的属性。在访问类的属性时,直接引用属性的名称即可,当然也可以加一个 self 前缀。
- 类的每一个实例都有一个隐含属性 self。self 完全等同于该实例本身,可以在类的实例方法中,使用 self 来引用实例本身,从而获得对实例其他属性和方法的访问。正由于是隐含的,这个 self 大部分情况下都可以省略。但是在类中,存在同名局部变量的情况下,self 就不能省略。
-
2)类型方法:类型方法不属于任何一个类的对象,而是属于公共的类,可以通过类名直接调用。但是正是如此,他也不能调用变量属性,因为使用类型方法,并不需要初始化类,没有空间存储对象,因而也并没有空间来保存变量。在类型方法的定义中,使用 self 是不允许的。类型方法的定义只需在定义的 func 前面加上 class 即可。
class func localizedStringWithFormat(format: NSString, _ args: CVarArgType...) -> Self
-
3)初始化方法:是在类初始化为对象时调用的方法。在创建对象之后,使用对象的属性之前,我们得做好相应的属性初始化,否则在没有初始化的前提下,就会报错。遇到这种情况时,一种解决方案是在定义变量时就做好初始化,另一种解决方案就是使用初始化方法。
- 初始化方法,同普通方法不同,方法名是固定的 init,也没有返回值,方法名 init 前面也不加 func 关键字。初始化方法可以接受参数,init 初始化方法的参数,在创建对象时就可以传入,不过同函数不太一样的是,调用时,每一个参数的变量名都需要加上,而普通函数,第一个是不需要写的。
init() {
width = 0
height = 0
}
var rect = Rect()
init(w:Int, h:Int) {
width = w
height = h
}
var rect = Rect(w:10, h:20)
初始化方法可以有多个,并且每一个在方法名上都一样,都是 init,但是参数上要有所不同。同其他语言不同,Swift 语言中,对于方法参数的不同,不仅仅是通过参数类型来判定的,还涉及参数的名称。两个方法相同,尽管参数数据类型一样,但是参数名不同,就是可以同时存在。
-
<1>、默认初始化方法:Swift 语言将为所有属性已提供默认值的且自身没有定义任何构造器的结构体的基类提供一个默认的初始化方法。
- 第一种情况,对存储属性都初始化了之后,可以用默认初始化方法实例化对象。
- 第二种情况,对类存储属性设置为可选存储属性,可以用默认初始化方法实例化对象。
- 第三种情况,对类存储属性设置为强制解包可选,可以使用默认初始化方法实例化对象。
-
<2>、便利初始化方法:便利初始化方法的出现正像他的名字,是为了便利我们调用而出现的。在他的实现里面,必须调用一个其他的初始化方法,便利初始化方法,在 init 前面加上 convenience 关键字即可。这些调用独立初始化方法的初始化方法,就叫便利初始化方法。
init(width:Int, height:Int) {
self.width = width
self.height = height
}
convenience init(w:Int, h:Int) {
self.init(width:w, height:h)
}
-
<3>、闭包初始化方法:如果某个存储类型属性的默认值需要特别定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。闭包后需要加“( )”,这里用来告诉 Swift 需要立刻执行此闭包,否则会把闭包本身作为值赋给了属性。
var score:[Int] = {
var scores:[Int] = Array()
for m in 0...3 {
scores.append(m)
}
return scores
} ()
-
4)反初始化方法:我们假设一个场景,我要建立一个数据库访问类,在初始化时打开链接,如果程序退出,连接不释放,资源就会浪费。所以我们引入一种新的特殊方法,跟初始化方法相对,叫反初始化方法。反初始化方法的方法名为 deinit,在把对象设置为 nil 时,系统会自动调用反初始化方法。
deinit {
self.conn.close()
self.conn = nil
}
5)重载:在类中,这种相同名字不同参数的方法的写法有一个专门的术语来描述,叫做重载。在别的语言中,重载的情况只出现在类定义里,但是在 Swift 语言中,类中的实例方法可以重载,而全局的函数也可以重载,只要参数名或者参数别名不同就可以实现重载,而不用关心参数数据类型的相同与不同。