版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.07.28 |
前言
我是swift2.0的时候开始接触的,记得那时候还不是很稳定,公司的项目也都是用oc做的,并不对swift很重视,我自己学了一段时间,到现在swift3.0+已经出来了,自己平时也不写,忘记的也差不多了,正好项目这段时间已经上线了,不是很忙,我就可以每天总结一点了,希望对自己对大家有所帮助。在总结的时候我会对比oc进行说明,有代码的我会给出相关比对代码。
1. swift简单总结(一)—— 数据简单值和类型转换
2. swift简单总结(二)—— 简单值和控制流
3. swift简单总结(三)—— 循环控制和函数
4. swift简单总结(四)—— 函数和类
5. swift简单总结(五)—— 枚举和结构体
6. swift简单总结(六)—— 协议扩展与泛型
7. swift简单总结(七)—— 数据类型
8. swift简单总结(八)—— 别名、布尔值与元组
9. swift简单总结(九)—— 可选值和断言
10. swift简单总结(十)—— 运算符
11. swift简单总结(十一)—— 字符串和字符
12. swift简单总结(十二)—— 集合类型之数组
13. swift简单总结(十三)—— 集合类型之字典
14. swift简单总结(十四)—— 控制流
15. swift简单总结(十五)—— 控制转移语句
16. swift简单总结(十六)—— 函数
17. swift简单总结(十七)—— 闭包(Closures)
18. swift简单总结(十八)—— 枚举
19. swift简单总结(十九)—— 类和结构体
属性
属性将值跟特定的类、结构体或枚举关联,存储属性存储常量或者变量作为实例的一部分,计算属性计算一个值,计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
存储属性和计算属性通常用于特定类型的实例,但是,属性也可以直接用于类型本身,这种属性称为类型属性。另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作,属性观察器可以添加到自己写的存储属性上,也可以添加到父类继承的属性上。
下面从以下几点进行讲解属性。
- 存储属性
(Store Properties)
- 计算属性
(Computed Properties)
- 属性观察器
(Property Observers)
- 全局变量和局部变量
(Global and Local Variables)
- 类型属性
(Type Properties)
存储属性
一个存储属性就是存储在特定类或者结构体的实例里的一个常量或者变量,存储属性可以是变量存储属性,也可以是常量存储属性。
可以在定义存储属性的时候指定默认值,也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值。
struct FixedLengthRange {
var firstValue : Int
let length : Int
}
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
var range = FixedLengthRange(firstValue: 0, length: 3)
range.firstValue = 10
print(range.firstValue)
print(range.length)
}
}
下面看输出结果
10
3
这里,range
实例包含了一个名为firstValue
的变量存储属性和一个名为length
的常量存储属性,上面例子,length
在创建时候被赋值,因为它是常量存储属性,不能后来修改它的值。
1. 常量存储属性
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性。
let range = FixedLengthRange(firstValue: 0, length: 3)
//range设置了常量,尽管firstValue为变量,那么也不可以修改
range.firstValue = 10
这是因为结构体是值类型,当值类型的实例被声明为常量的时候,它的所有属性也就成了常量;而类是引用类型,把一个引用类型实例赋值给一个常量后,仍然可以修改实例的变量属性。
2. 延迟存储属性
延迟存储属性是指第一次被调用的时候才会计算其初始值的属性,在属性声明前使用lazy
来标示一个延迟存储属性。
注意:必须将延迟属性声明成变量(var关键字)
,因为属性的值再实例构造完成之前可能无法得到,而常量属性在构造过程完成之前必须要有初始值,因此不能声明成延迟属性。
当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它,这个时候都用延迟属性。
下面看这里例子。
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
let manager = DataManager()
manager.data.append("data")
manager.data.append("here")
print(manager.data)
}
}
//该类是一个将外部文件中的数据导入的类,提供了数据导入功能
class DataImporter {
var fileName = "data.txt"
}
//该类是数据管理功能
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
}
这里,DataManager
类包含一个data的存储属性,是一个空数组,它的功能就是从文件导入数据,该功能由类DataImporter
完成,DataManager
可能从文件中导入数据,也可能不从中导入数据,所以当DataManager
实例被创建时,没必要创建一个DataImporter
实例,更好的方式就是用到DataImporter
的时候再去创建它。由于使用了lazy
,所以importer
属性只有第一次被创建的时候才被创建。
3. 存储属性和实例变量
在OC
中我们可以有属性和实例变量,而swift
中把二者统一了起来,swift
中的属性没有对应的实例变量,属性的后端存储也无法直接访问,这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
计算属性
类、结构体和枚举还可以定义计算属性,计算属性不直接存储值,而是提供了一个getter
来获取值,一个可选的setter
来间接设置其它属性或变量的值。
下面看一个例子。
struct Point {
var x = 0.0
var y = 0.0
}
struct Size {
var width = 0.0
var height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center : Point{
get{
let centerX = origin.x + size.width * 0.5
let centerY = origin.y + size.height * 0.5
return Point(x: centerX, y: centerY)
}
set(newCenter){
origin.x = newCenter.x - size.width * 0.5
origin.y = newCenter.y - size.height * 0.5
}
}
}
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
var square = Rect(origin: Point(x : 0.0, y : 0.0), size: Size(width: 10.0, height: 10.0))
let initialCenter = square.center
print(initialCenter)
square.center = Point(x: 20.0, y: 20.0)
print("square.origin is at (\(square.origin.x), \(square.origin.y))")
}
}
下面看输出结果
Point(x: 5.0, y: 5.0)
square.origin is at (15.0, 15.0)
这里Rect
中的center
就是计算属性,
1. 便携setter声明
如果计算属性的setter没有定义表示新值的参数名,则可以使用默认名称newValue
,下面是使用了便携setter声明的例子。所以我们的Rect
结构体也可以这么写。
struct Rect {
var origin = Point()
var size = Size()
var center : Point{
get{
let centerX = origin.x + size.width * 0.5
let centerY = origin.y + size.height * 0.5
return Point(x: centerX, y: centerY)
}
set{
origin.x = newValue.x - size.width * 0.5
origin.y = newValue.y - size.height * 0.5
}
}
}
2. 只读计算属性
只有getter
没有setter
的计算属性就是只读计算属性,只读属性总是返回一个值,可以通过点运算访问,不能设置新值,估计大家知道OC
中的属性修饰符readonly
,它们是类似的。
只读计算属性声明可以去掉get
关键字和花括号。看下面的简单例子。
struct Cuboid {
var width = 0.0
var height = 0.0
var depth = 0.0
var volume : Double {
return width * height * depth
}
}
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
let volumeResult = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print(volumeResult.volume)
}
}
下面看输出结果
40.0
这里的体积就是getter直接返回就可以。
属性观察器
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外,可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加观察器。
注意:不需要为无法重载的计算属性添加属性观察器,因为可以通过setter
直接监控和响应值的变化。
可以为属性添加如下的观察器
-
willSet
在设置新值之前调用 -
didSet
在新的值被设置之后立即调用
willSet
观察器会将新的属性值作为固定参数传入,在willSet
的代码实现中可以为这个参数指定一个名称,也可以不指定,这时使用默认名称newValue
表示。同理。didSet
观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue
。
注意:willSet
和didSet
观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
下面看个简单例子。
class StepCounter{
var totalSteps : Int = 0 {
willSet(newValue){
print("set totalSteps to \(newValue)")
}
didSet(oldValue){
if totalSteps > oldValue {
print("Add \(totalSteps - oldValue) steps")
}
}
}
}
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
stepCounter.totalSteps = 360
stepCounter.totalSteps = 900
}
}
下面看输出结果
set totalSteps to 200
Add 200 steps
set totalSteps to 360
Add 160 steps
set totalSteps to 900
Add 540 steps
这里,willSet
和didSet
都定义了变量,如果不定义就用默认值newValue
和oldValue
。
如果在didSet
观察器里为属性赋值,这个值会替换观察器之前设置的值。
全局变量和局部变量
计算属性和属性观察器描述的模式也可以用于全局变量和局部变量,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。
注意:全局常量或变量都是延迟计算的,跟延迟存储属性相似,不同地方在于,全局的常量或变量不需要标记lazy
特性。局部范围的常量或变量不会延迟计算。
类型属性
实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立,也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份,这种属性就是类型属性,类型属性用于定义特定类型所有实例共享的数据。对于值类型(结构体和枚举)可以定义存储型和计算型属性,对于类(class)则只能定义计算型类型属性,值类型的存储类型属性可以是变量或常量,计算型类型属性和实例的计算属性一样定义成变量属性。
注意:跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
1. 类型属性语法
在C
和OC
中,静态常量和静态变量的定义是通过特定类型加上global
关键字,在swift
中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。利用关键字static
来定义值类型的类型属性,关键字class
来为类定义类型属性。
看下面几个例子。
struct SomeStruct {
static var storedTypeProperty = "Some value"
static var computedTypeProperty : Int {
return 100
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value"
static var computedTypeProperty : Int {
return 150
}
}
class SomeClass {
class var computedTypeProperty : Int {
return 200
}
}
2. 获取和设置类型属性的值
跟实例的属性一样,类型属性的访问也是通过点运算来运行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例,比如:
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
print(SomeClass.computedTypeProperty)
print(SomeStruct.storedTypeProperty)
SomeStruct.storedTypeProperty = "Another value"
print(SomeStruct.storedTypeProperty)
}
}
下面看输出结果
200
Some value
Another value
再看一下下面的例子。
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel : Int = 0 {
didSet{
if currentLevel > AudioChannel.thresholdLevel {
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}
下面我们调用下代码,查看结果。
class JJSwiftVC: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
print(AudioChannel.maxInputLevelForAllChannels)
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
print(AudioChannel.maxInputLevelForAllChannels)
}
}
下面看输出结果
7
7
10
10
后记
未完,待续~~~~