swift中的声明关键字详解

11次阅读

共计 7505 个字符,预计需要花费 19 分钟才能阅读完成。

原起

学习 swift,swift 中的关键字当然要了解清楚了,最近在网上看到了关于声明关键字的文章,整理记录一下。

关键字是类似于标识符的保留字符序列,除非用重音符号(`)将其括起来,否则不能用作标识符。关键字是对编译器具有特殊意义的预定义保留标识符。

常见的关键字有以下 4 种:

  • 与声明有关的关键字:class、deinit、enum、extension、func、import、init、let、protocol、static、struct、subscript、typealias 和 var。
  • 与语句有关的关键字:break、case、continue、default、do、else、fallthrough、if、in、for、return、switch、where 和 while。
  • 表达式和类型关键字:as、dynamicType、is、new、super、self、Self、Type、__COLUMN__、__FILE__、__FUNCTION__和__LINE__。
  • 在特定上下文中使用的关键字:associativity、didSet、get、infix、inout、left、mutating、none、nonmutating、operator、override、postfix、precedence、prefix、rightset、unowned、unowned(safe)、unowned(unsafe)、weak 和 willSet。

声明关键字一览图

swift 常见的声明关键字整理如下(不想看长文的,直接看下图即可)

声明关键字详解

1、class

在 swift 中,我们使用 class 关键字去声明一个类或者类方法。

class Person: NSObject {

    
    /// add `class` key word before function, this function become a class function
    class func work(){print("everyone need work!")
    }
}

这样我们就声明了一个 Person 类。

2、let

swift 里有 let 关键字声明一个常量,及我们不可以对他进行修改。(注意:我们用 let 修饰的常量是一个类, 我们可以对其所在的属性进行修改

class iOSer: Person{
    let name: String = "ningjianwen"
    var age: Int = 30
    var height: Float = 170
}

let ITWork: iOSer = iOSer()
ITWork.age = 25
print("老子希望永远 25 岁")

在 iOSer 类中 let 声明的 name 不可修改,var声明的 age&height 可以修改。同时 let 关键字声明的 ITWork 实例不可变,但是内部的 var 关键字声明的刷新是可以修改的。

3、var

swift 中 var 修饰的变量是一个可变的变量,可以对她的值进行修改。
注意:我们不会用 var 去引用一个类, 也没有必要。

func iOSerClassFunction(){let ITWork: iOSer = iOSer()
        ITWork.age = 25
        print("老子希望永远 \(ITWork.age)岁")
        
        let iOS1 = ITWork
        iOS1.age = 18
        print("iOS1 age =\(iOS1.age)")
        print("ITWork age = \(ITWork.age)")
    }
    /** 打印结果
    老子希望永远 25 岁
    iOS1 age =18
    ITWork age = 18
    */

从结果可以看出对 iOS1 的修改同样影响了ITWork,说明两个对象指向同一块内存空间。

4、struct

在 Swift 中, 我们使用 struct 关键字去声明结构体,Swift 中的结构体并不复杂,与 C 语言的结构体相比,除了成员变量,还多了成员方法。使得它更加接近于一个类。可以把 struct 看作是类的一个轻量化实现。

struct Student {
    var name: String
    var age: Int
    
    func introduce(){print("我叫:\(name), 今年 \(age)岁")
    }
}

从上方的代码可以看出,结构体和类拥有相同的功能,可以定义属性和方法。但是二者的内存管理方式有所不同,class 属于引用类型,而 struct 属于值类型。
swift 中的结构体语法上与 C 语言或者 OC 类似,只不过 Swift 中的结构体,在定义成员变量时一定要注明类型。

5、enum

在 Swift 中, 我们使用 enum 关键字去声明枚举。枚举是一种常见的数据类型,他的主要功能就是将某一种有固定数量可能性的变量的值,以一组命名过的常数来指代。比如正常情况下方向有四种可能,东,南,西,北。我们就可以声明一组常量来指代方向的四种可能。使用枚举可以防止用户使用无效值,同时该变量可以使代码更加清晰。

enum Orientation: Int{
    case East
    case South
    case West
    case North
        
        /**
         或者
         enum Orientation:Int{case East,South,West,North}
         */
}

print(Orientation.East.rawValue)
/**
 输出结果 0
 
 */

注意:我们在定义枚举时,一定要指定类型,否则在使用时就会报错。枚举类型的值如果没有赋值,他就按照默认的走,可以赋予我们自己想要的值。

6、final

final 关键字可以在 classfuncvar 前修饰,表示不可重写,可以把类或者类中的部分实现保护起来,从而避免子类破坏。

class Fruit : NSObject {
    // 修饰词 final 表示 不可重写 可以将类或者类中的部分实现保护起来, 从而避免子类破坏
    final func price(){
        //something price code here
        //...
    }
}

class Apple: Fruit {
//    此处重写 `final` 修饰的 price 方法,报错“Instance method overrides a 'final' instance method”//    override func price(){
//
//    }
}

7、override

在 Swift 中, 如果我们要重写某个方法, 或者某个属性的话, 我们需要在重写的变量前增加一个 override 关键字。

class Fruit : NSObject {
    
    var sellPrice: Double = 0.0
    var name: String = "fruit"
    func info(){print("this fruit name is fruit")
    }
    
    // 修饰词 final 表示 不可重写 可以将类或者类中的部分实现保护起来, 从而避免子类破坏
    final func price(){
        //something price code here
        //...
    }
}

class Apple: Fruit {func eat(){print("eat fruit")
    }
    // 重写 info 方法
    override func info() {print("this fruit name is \(super.name)")
    }
    // 重写 name 属性
    override var name: String{
        set{super.name = newValue}
        get{return super.name}
    }
}

8、subscript

在 swift 中,subscript关键字表示下标,可以让 classstruct、以及enum 使用下标访问内部的值。其实就可以快捷方式的设置或者获取对应的属性, 而不需要调用对应的方法去获取或者存储, 比如官网的一个实例:

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    
    
    func indexIsValid(row: Int, column: Int) -> Bool {return row >= 0 && row < rows && column >= 0 && column < columns}
    // 实现 `subscript` 方法
    subscript(row: Int, column: Int) -> Double {
        get {assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

func matrixTest(){var matrix = Matrix(rows: 2, columns: 2)
        matrix[0, 1] = 1.5
        matrix[1, 0] = 3.2
        print("matrix == \(matrix)")
        /**
         打印结果:matrix == Matrix(rows: 2, columns: 2, grid: [0.0, 1.5, 3.2, 0.0])
         */
    }

9、static

在 swift 中,我们用 static 关键字声明静态变量或者函数,它保证在对应的作用域当中只有一份, 同时也不需要依赖实例化。
注意:(用 static 关键字指定的方法是类方法,他是不能被子类重写的

10、mutating

mutating关键字指的是可变即可修改。用在 structure 和 enumeration 中, 虽然结构体和枚举可以定义自己的方法,但是默认情况下,实例方法中是不可以修改值类型的属性。为了能够在实例方法中修改属性值,可以在方法定义前添加关键字 mutating。

11、typealias

使用关键字 typealias 定义类型别名 (typealias 就相当于 objective- c 中的 typedef),就是将类型重命名,看起来更加语义化。说人话就是: 起别名

typealias Width = Float
typealias Height = Float
func rectangularArea(width:Width, height:Height) -> Double {return Double(width*height)
        }

12、lazy

lazy 关键修饰的变量, 只有在第一次被调用的时候才会去初始化值(即懒加载)。这个在定义属性经常会用到,为了提高程序的性能,我们把它定义成 lazy 的,等待真正需要的时候采取加载。

lazy var titleLabel: UILabel = {var lab = UILabel()
       lab.frame = CGRect(x: 50, y: 100, width: 200, height: 20)
       lab.textAlignment = .center
       lab.font = UIFont.systemFont(ofSize: 18)
       lab.textColor = UIColor.blue
       return lab
    }()

13、init

init关键字也表示构造器,初始化方法,在 init 后面加个”?”号, 表明该构造器可以允许失败。

class PerSon {
        var name:String
        init?(name : String) {if name.isEmpty { return nil}
            self.name = name
        }
    }

14、required

required是用来修饰 init 方法的,说明该构造方法是必须实现的。

class Father: NSObject {
    var name: String?
    required init(name: String) {self.name = name}
}

class Son: Father {required init(name: String) {super.init(name: name)
        self.name = name
    }
}

从上面的代码示例中不难看出,如果子类需要添加异于父类的初始化方法时,必须先要实现父类中使用 required 修饰符修饰过的初始化方法,并且也要使用 required 修饰符而不是 override。

使用 required 的注意点:

  1. required 修饰符只能用于修饰类初始化方法。
  2. 当子类含有异于父类的初始化方法时(初始化方法参数类型和数量异于父类),子类必须要实现父类的 required 初始化方法,并且也要使用 required 修饰符而不是 override。
  3. 当子类没有初始化方法时,可以不用实现父类的 required 初始化方法。

15、extension

extension 与 Objective- C 的 category 有点类似,但是 extension 比起 category 来说更加强大和灵活 ,它不仅可以扩展某种类型或结构体的方法,同时它还可以与 protocol 等结合使用,编写出更加灵活和强大的代码。它可以为特定的class, strut, enum 或者 protocol 添加新的特性。当你没有权限对源代码进行改造的时候,此时可以通过 extension 来对类型进行扩展。extension 有点类似于 OC 的类别 — category,但稍微不同的是 category 有名字,而 extension 没有名字。

extension 也是 swift 开发经常用到的,它可以扩展扩展以下几个:

    1. 定义实例方法和类型方法
    1. 添加计算型属性和计算静态属性
    1. 定义下标
    1. 提供新的构造器
    1. 定义和使用新的嵌套类型
    1. 使一个已有类型符合某个接口
// MARK: - 添加计算属性
extension Double{var km: Double { return self * 1_000.0}
    var m: Double {return self}
    var cm: Double {return self / 100.0}
}


// MARK: - 为 person 添加方法
extension Person{func run(){print("人有行走的属性")
    }
}

调用方法:func extensionTest(){
        
        let oneInch = 25.4.km
        print("One inch is\(oneInch) meter")
        
        let njw = Person()
        njw.run()}
    /** 打印结果
    One inch is25400.0 meter
    人有行走的属性
    */

16、convenience

使用 convenience 修饰的构造函数叫做 便利构造函数 。便利构造函数通常用在对系统的类进行构造函数的扩充时使用。
便利构造函数的特点如下:

    1. 便利构造函数通常都是写在 extension 里面
    1. 便利函数 init 前面需要加载 convenience
    1. 在便利构造函数中需要明确的调用 self.init()

17、deinit

deinit 属于 析构函数 ,当对象结束其生命周期时(例如对象所在的函数已调用完毕), 系统自动执行析构函数 。和 OC 中的 dealloc 一样的。
我们通常在 deinit 函数中进行一些资源释放和通知移除等。
列举如下:

    1. 对象销毁
    1. KVO 移除
    1. 移除通知
    1. NSTimer 销毁

18、fallthrough

在 swift 中,fallthrough 的作用是就是在 switch-case 中执行完当前 case, 继续执行下面的 case.

19、protocol

在 swift 中,protocol 也是定义协议的,用法和 OC 类似。

20、open

open 修饰的对象表示可以被任何人使用,包括 override 和继承。

21、public

在 swift 中,public表示公有访问权限,类或者类的公有属性或者公有方法可以从文件或者模块的任何地方进行访问。但在其他模块 (一个 App 就是一个模块,一个第三方 API, 第三等方框架等都是一个完整的模块) 不可以被 override 和继承,而在本模块内可以被 override 和继承。

22、internal

在 swift 中,internal表示内部的访问权限。即有着 internal 访问权限的属性和方法说明在模块内部可以访问,超出模块内部就不可被访问了。在 swift 中默认就是 internal 的访问权限。

23、private

在 swift 中,private表示私有访问权限。被 private 修饰的类或者类的属性或方法可以在同一个物理文件中访问。如果超出该物理文件,那么有着 private 访问权限的属性和方法就不能被访问。

24、fileprivate

在 swift 中,fileprivate 访问级别所修饰的属性或者方法在当前的 Swift 源文件里可以访问。

下面的代码是对 internal,private,fileprivate 的一个简单展示。

/// 默认是 internal 的访问权限,在模块内部可以访问
class ParentClass: NSObject {
    
    /// 这个方法在如何地方可以被 `override`
    func speak(){print("这是一个说话属性,子类可以进行复写")
    }
    
    /// 这个方法是秘密,只有父类拥有,子类不可修改
    private func secret(){print("这是一个秘密,只有我自己知道")
    }
    
    /// 这是本类的秘密,出了该类就看不到了
    fileprivate func localSecret(){print("这是本类的秘密,出了该类就看不到了")
    }
}

class FirstSon: ParentClass {
    
    /// 长子说话
    override func speak() {print("我是长子")
    }
// 长子也不能修改老爸的秘密
//    override func secret(){
//
//    }
    
    override func localSecret() {print("儿子把家里的秘密说出去了")
    }
}
// 方法调用与及结果打印
func parentClassTest(){let parent: ParentClass = ParentClass()
        parent.speak()
//        parent.secret() // 老爸的秘密不能对外
//        parent.localSecret() // 家里的秘密也不能对外
        let oldSon: FirstSon = FirstSon()
        oldSon.speak()
//        oldSon.secret() // 儿子不能说老爸的秘密
        oldSon.localSecret() // 儿子把家里的秘密说出去了
        /** 打印结果
         
         这是一个说话属性,子类可以进行复写
         我是长子
         儿子把家里的秘密说出去了
         */
    }

说明:5 种修饰符访问权限排序 open > public > internal > fileprivate > private.
从类的安全性上来说,访问权限越小越好

SwiftStatementDemo

参考:swift 中的关键字详解

正文完
 0