Swift-关键字

作者: 物非0人非 | 来源:发表于2021-08-05 09:19 被阅读0次

    Swift关键字汇总如下:

    一: 常见使用频繁的关键字:
    1,访问修饰符:在swift中,访问修饰符有五种,分别是:open、public 、internal、fileprivate、private

    从高到低的排序:open > public >interal > fileprivate > private
    open:可以被任何模块的代码访问,包括override(重写)和继承。
    public: 可以被任何模块的代码访问,模块内是可以被override(重写)和继承的,而在但其他模块不可以override(重写)和继承。
    internal:内部的意思。默认访问级别,可写可不写,访问级别所修饰的属性或方法在源代码所在的整个模块都可以访问。
    fileprivate:修饰的属性或者方法只能在当前文件中访问,包括override(重写)和继承,当前类的extension 中也可以。如果一个文件中含有多个类,这些类中也可以。

    class Person  
    {  
        fileprivate var jobTitle:String = ""  
    }
    extension Person  
    {
        //当 extension 和 class 在同一个文件中时,允许访问
        func printJobTitle()  
        {  
            print("My job is \(jobTitle)")  
        }  
    }
    

    private:修饰的属性或者方法只能在当前类中访问,当前类的extension 中也可以访问。如果当前文件有多个类,这些类中不可以访问。

    image.png
    2,其他修饰符

    Any:用于表示任意类型的实例,包括函数类型。
    AnyObject 可以代表任何 class 类型的实例。
    override:指明子类会提供自定义实现,覆盖父类的实例方法、类型方法、实例属性、类型属性、下标。如果没有实现,则会直接继承自父类。

    class Person  
    {  
        func printInfo()  
        {  
            print("I'm just a person!")  
        }  
    }
    
    class Programmer : Person  
    {  
        override func printInfo()  
        {  
            print("I'm a person who is a dev!")  
        }  
    }
    
    let aPerson = Person()  
    let aDev = Programmer()
    
    aPerson.printInfo() //打印 I'm just a person!  
    aDev.printInfo() //打印 I'm a person who is a dev!
    

    ?!,Swift语言中对一种可选类型( Optional) 操作的语法糖。
    一句话:确定有值直接使用!,不确定有值使用

    ?: 先判断在拆包的语法糖。Swift中是可以声明一个没有初始值的属性,类型后加加一个 ? 操作符完成的。

    var name: String?
    // 上面这个Optional的声明,是”我声明了一个Optional类型值,
     它可能包含一个String,也可能什么都不包含
    

    !:表示强解包,不管有没有值直接拆包。如果没有值往往会崩溃crash,所有使用的时候一定要确定有值。

    //举例1
    // 定义一个string的可选类型str,显式赋值
    var str: String? = "Hello"
    if str{
        let message = "你好" + str!
        print(message)//因为已经给str赋值为hello,所以if判断条件成立,然后给name强解包,得到hello
    }
    
    //举例2
    // 定义一个string的可选类型str,但是没有显式赋值
    var str: String?
    let message = "你好" + str!
    print(message)//强解包程序会crash掉。所以在使用!强解包的时候一定要确定解包对象不为nil,否则会引起程序崩溃。
    

    例如
    try:表明接着调用的函数可能会抛出 error。有三种不同的使用方式:try(默认tr),try?(可选try), try!( 强行try )。
    使用如下几种,建议示例使用法:

    import UIKit
    
    /* 知识点:
     从对象 到 二进制数据的过程 一般称为对象的序列化(Serialization),也称为归档(Archive).
     同理,从二进制数据 到 对象的过程 一般称为反序列化或者反归档.
     */
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            loadData()
        }
        
        // 请求数据
        func loadData(){//
            // 请求的url (实测: 返回结果为json)
            let url = URL(string: "http://www.weather.com.cn/data/sk/101010100.html")
    //        let url = URL(string: "http://www.baidu.com")
            
            // guard判断url 是否为nil
            guard let u = url else {
                return
            }
            
            // 实例化request
            let request = URLRequest(url: u)
            
            // 发送请求
            URLSession.shared.dataTask(with: request) { (data, _, error) in
                
                // 错误判断
                if error != nil {
                    print(error!)
                    return
                }
                
                // 判断data 是否有值
                guard let d = data else{
                    return
                }
    
                // 出现问题: 正常情况下, 将Json 反序列化为字典, 但是正常写了会报错
                // 报错内容: Call can throw, but it is not marked with 'try' and the error is not handled
                // 报错原因: 调用可能够抛出异常, 因为没有标记try, 也未作错误处理. 系统处理的一种方式而已.
                // 解决方法: 既然说了没有标记try, 那就标记即可.
    //            let dict = JSONSerialization.jsonObject(with: d, options: [])
                
                // 解决方式一: 强行try (try!)
                // 存在问题: 当请求结果不是标准的json数据时, 会造成程序崩溃
                // 也就是说: url为天气的链接时, 就会正常使用; 而url为百度的链接时, 就会崩溃.
    //            let dict = try! JSONSerialization.jsonObject(with: d, options: [])
                
                
                // 解决方式二: 可选try (try?)
                // 特点: 能反序列化成功, 就给你返回成功的值; 不能成功就给你返回nil
    //            let dict = try? JSONSerialization.jsonObject(with: d, options: [])
                
                // 打印结果
    //            print(dict)
                
                // 解决方式三: 默认try (try)
                // 注意: 一定要配合 do{}catch{} 使用
                do{
                    let dict = try JSONSerialization.jsonObject(with: d, options: [])
                    print(dict)
                }catch{
                    // catch 中默认提供error信息, 当序列化不成功是, 返回error
                    print(error)
                }
            }.resume()
        }
    }
    

    as:类型转换运算符,用于尝试将值转成其它类型。 as、as!、as?三种
    as的使用:
    ①数值类型转换
    示例1:

    let age = 28 as Int
    let money = 20 as CGFloat
    let cost = (50 / 2) as Double
    
    

    ②从派生类转换为基类,向上转型:

    // 1,定义人员基类
    class Person {
        var name : String
        
        init(_ name: String){
            self.name = name
        }
    }
    
    // 2,定义学生类
    class Student : Person {
    }
    
    // 3,定义教师类
    class Teacher : Person {
    }
    
    // 处理人员对象的函数(或工厂模式处理操作等)
    func showPersonName(_ people : Person){
        let name = people.name
        print("这个人的名字是: \(name)")
    }
    
    // 定义一个学生对象 tom
    var tom = Student("Tom");
    
    // 定义一个教师对象 kevin
    var kevin = Student("Kevin Jakson");
    
    // 先把学生对象向上转型为一般的人员对象
    let person1 = tom as Person
    let person2 = kevin as Person
    
    // 再调用通用的处理人员对象的showPersonName函数
    showPersonName(person1)//这个人的名字是: Tom
    showPersonName(person2)//这个人的名字是: Kevin Jakson
    

    ③switch 语句中进行模式匹配
    通过switch语法检测对象的类型,根据对象类型进行处理

    switch person1 {
        case let person1 as Student:
            print("是Student类型,打印学生成绩单...")
        case let person1 as Teacher:
            print("是Teacher类型,打印老师工资单...")
        default: break
    }
    

    as!的使用:

    • as!:向下转型(Downcasting)时使用。由于是强制类型转换,如果转换失败会报 runtime 运行错误。
    let person : Person = Teacher("Jimmy Lee")
    let jimmy = person as! Teacher
    

    as?的使用:

    • as? : as? 和 as! 操作符的转换规则完全一样。但 as?如果转换不成功的时候便会返回一个nil 对象。成功的话返回可选类型值。由于 as? 在转换失败的时候也不会出现错误,所以对于如果能确保100%会成功的转换则可使用 as!,否则使用 as?
    let a = 13 as! String
    print(a)
    //会crash
    
    let a = 13 as? String
    print(a)
    //输出为nil
    

    is:类型检查运算符,用于确定实例是否为某个子类类型。

    class Person {}  
    class Programmer : Person {}  
    class Nurse : Person {}
    
    let people = [Programmer(), Nurse()]
    
    for aPerson in people  
    {  
        if aPerson is Programmer  
        {  
            print("This person is a dev")  
        }  
        else if aPerson is Nurse  
        {  
            print("This person is a nurse")  
        }  
    }
    

    _:用于匹配或省略任意值的通配符。
    1,格式化数字字面量,提高数字字面量的可读性

    let paddedDouble = 123.000_001
    let oneMillion = 1_000_000
    

    2,忽略元组的元素值。假设有的元素不须要使用,使用下划线将对应的元素进行忽略

    // 仅关心http404Error中第二个元素的值。所以第一个元素能够使用下划线进行忽略
    let http404Error = (404, "Not Found")
    let (_, errorMessage) = http404Error
    

    3,忽略区间值

    let base = 3
    let power = 10
    var answer = 1
    for _ in 1...power {
        answer *= base
    }
    

    4,忽略外部參数名
    (1).忽略方法的默认外部參数名

    class Counter {
        var count: Int = 0
        func incrementBy(amount: Int, numberOfTimes: Int) {
            count += amount * numberOfTimes
        }
    }
    /* 在上面的代码中,方法incrementBy()中的numberOfTimes具有默认的外部
    參数名:numberOfTimes,假设不想使用外部參数名能够使用下划线进行
    忽略,代码能够写为(只是为了提高代码的可读性,一般不进行忽略)*/
    class Counter {
        var count: Int = 0
        func incrementBy(amount: Int, _ numberOfTimes: Int) {
            count += amount * numberOfTimes
        }
    }
    

    (2).忽略具有默认值的參数的外部參数名

    //   假设不想使用默认外部參数名,能够进行例如以下改动:
    func join(_ s1: String, _ s2: String, _ joiner: String = " ") -> String {
        return s1 + joiner + s2
    }
    // call the function.
    join("hello", "world", "-")
    

    inout:将一个值传入函数,并可以被函数修改,然后将值传回到调用处,来替换初始值。适用于引用类型和值类型。 其实就是声明参数为指针

    func dangerousOp(_ error:inout NSError?)  
    {  
        error = NSError(domain: "", code: 0, userInfo: ["":""])  
    }
    
    var potentialError:NSError?
    dangerousOp(&potentialError)
    
    //代码运行到这里,potentialError 不再是 nil,而是已经被初始化
    
    

    do:用于表示处理错误代码段的开始。
    final:防止方法、属性、下标被重写。
    get:返回成员的值。还可以用在计算型属性上,间接获取其它属性的值。
    default:用于涵盖在 switch 语句中,所有未明确列出的枚举成员。
    optional:用于指明协议中的可选方法。遵守该协议的实体类可以不实现这个方法。
    #if:条件编译控制语句,用于控制程序在不同条件下编译代码。通过判断条件,决定是否执行代码。
    #elseif:条件编译控制语句,用于控制程序在不同条件下执行代码。与 #if 语句结合使用。当条件为 true,执行对应代码。
    #endif:条件编译控制语句,用于控制程序在不同条件下执行代码。用于表明条件编译代码的结尾。
    willSet:属性观察者,在值存储到属性之前调用。
    didSet:属性观察者,当值存储到属性后马上调用。

        
        //带属性监视器的普通属性
        var age:Int = 0 {
            //我们需要在age属性变化前做点什么
            willSet {
                println("Will set an new value \(newValue) to age")
            }
            //我们需要在age属性发生变化后,更新一下nickName这个属性
            didSet {
                println("age filed changed form \(oldValue) to \(age)")
                if age < 10 {
                    nickName = "Little"
                }else{
                    nickName = "Big"
                }
            }
        }
    

    deinit: 当一个类的实例即将被销毁时,会调用这个方法。

    class Person  
    {  
        var name:String  
        var age:Int  
        var gender:String
    
        deinit  
        {  
            //从堆中释放,并释放的资源
        }  
    }
    
    

    required:确保编译器会检查该类的所有子类,全部实现了指定的构造器方法。**
    required关键字

    对于某些我们希望子类中一定实现的designated初始化方法,我们可以通过添加required关键字进行限制,强制子类对这个方法重写。
    required修饰符的使用规则:

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

    代码:

            class MyClass {
                var str:String
                required init(str:String) {
                    self.str = str
                }
            }
            class MySubClass:MyClass
            {
                init(i:Int) {
                    super.init(str:String(i))
                }
    
            }
            // 编译错误
            MySubClass(i: 123)
    
    

    会报错,因为你没有实现父类中必须实现的方法。正确的写法:

        class MyClass{
            var str: String?
            required init(str: String?) {
                self.str = str
            }
        }
        class MySubClass: MyClass{
            init(i: Int) {
                super.init(str: String(i))
            }
    
            required init(str: String?) {
                fatalError("init(str:) has not been implemented")
            }
        }
    
    

    从上面的代码中,不难看出子类需要添加异于父类的初始化方法,必须要重写有required的修饰符的初始化方法,并且也要使用required修饰符而不是override,请千万注意!

    如果子类中并没有不同于父类的初始化方法,Swift会默认使用父类的初始化方法:

        class MyClass{
            var str: String?
            required init(str: String?) {
                self.str = str
            }
        }
        class MySubClass: MyClass{
        }
        var MySubClass(str: "hello swift")
    
    

    在这种情况下,编译器不会报错,因为如果子类没有任何初始化方法时,Swift会默认使用父类的初始化方法。

    nil:在 Swift 中表示任意类型的无状态值。

    Swift的nil和OC中的nil不一样.在OC中,nil是一个指向不存在对象的指针。
    而在Swift中,nil不是指针,它是一个不确定的值。用来表示值缺失.任何类型的optional都可以被设置为nil.。而在OC中,基本数据类型和结构体是不能被设置为nil的. 给optional的常量或者变量赋值为nil.来表示他们的值缺失情况.一个optional常量或者变量如果在初始化的时候没有被赋值,他们自动会设置成nil.

    class Person{}  
    struct Place{}
    
    //任何 Swift 类型或实例可以为 nil
    var statelessPerson:Person? = nil  
    var statelessPlace:Place? = nil  
    var statelessInt:Int? = nil  
    var statelessString:String? = nil
    

    guard:当有一个以上的条件不满足要求时,将离开当前作用域。同时还提供解包可选类型的功能。

       guard x > 0 else {
            // 变量不符合条件判断时,执行下面代码
            return
        }
    

    1.guard关键字必须使用在函数中。
    2.guard关键字必须和else同时出现。
    3.guard关键字只有条件为false的时候才能走else语句 相反执行后边语句。

    indirect
    指明在枚举类型中,存在成员使用相同枚举类型的实例作为关联值的情况。

    indirect enum Entertainment  
    {  
       case eventType(String)  
       case oneEvent(Entertainment)  
       case twoEvents(Entertainment, Entertainment)  
    }
    
    let dinner = Entertainment.eventType("Dinner")  
    let movie = Entertainment.eventType("Movie")
    
    let dateNight = Entertainment.twoEvents(dinner, movie)
    
    

    lazy
    指明属性的初始值,直到第一次被使用时,才进行初始化。

    class Person  
    {  
        lazy var personalityTraits = {  
            //昂贵的数据库开销  
            return ["Nice", "Funny"]  
        }()  
    }
    let aPerson = Person()  
    aPerson.personalityTraits //当 personalityTraits 首次被访问时,数据库才开始工作
    
    

    optional
    用于指明协议中的可选方法。遵守该协议的实体类可以不实现这个方法。

    @objc protocol Foo  
    {  
        func requiredFunction()  
        @objc optional func optionalFunction()  
    }
    
    class Person : Foo  
    {  
        func requiredFunction()  
        {  
            print("Conformance is now valid")  
        }  
    }
    
    

    extension:允许给已有的类、结构体、枚举、协议类型,添加新功能。

    class Person  
    {  
        var name:String = ""  
        var age:Int = 0  
        var gender:String = ""  
    }
    
    extension Person  
    {  
        func printInfo()  
        {  
            print("My name is \(name), I'm \(age) years old and I'm a \(gender).")  
        }  
    }
    

    .Type:表示任意类型的类型(就是一个元类型(Meta),包括类类型、结构类型、枚举类型、协议类型。
    .self:.self 用在类型后面取得类型本身,用在实例后面取得实例本身。

    二: 其他的关键字:

    super:在子类中,暴露父类的方法、属性、下标
    infix:指明一个用于两个值之间的运算符。如果一个全新的全局运算符被定义为 infix,还需要指定优先级。
    associativity:指明同一优先级的运算符,在缺少大括号的情况,按什么顺序结合。使用 left、right、none。
    associatedtype:在协议中,定义一个类型的占位符名称。直到协议被实现,该占位符才会被指定具体的类型。
    indirect:指明在枚举类型中,存在成员使用相同枚举类型的实例作为关联值的情况。
    in:在序列上迭代,比如一组特定范围内的数字,数组中的元素,字符串中的字符。与关键字 key 搭配使用。*
    left:指明运算符的结合性是从左到右。在没有使用大括号时,可以用于正确判断同一优先级运算符的执行顺序。
    mutating:允许在方法中修改结构体或者枚举实例的属性值。
    none:是一个没有结合性的运算符。不允许这样的运算符相邻出现。
    nonmutating:指明成员的 setter 方法不会修改实例的值,但可能会有其它后果。
    postfix:位于值后面的运算符。
    precedence:指明某个运算符的优先级高于别的运算符,从而被优先使用。
    prefix:位于值前面的运算符。
    right:指明运算符的结合性是从右到左的。在没有使用大括号时,可以用于正确判断同一优先级运算符的顺序。
    set:通过获取的新值来设置成员的值。同样可以用于计算型属性来间接设置其它属性。如果计算型属性的 setter 没有定义新值的名称,可以使用默认的 newValue。
    unowned:让循环引用中的实例 A 不要强引用实例 B。前提条件是实例 B 的生命周期要长于 A 实例。
    weak:允许循环引用中的实例 A 弱引用实例 B ,而不是强引用。实例 B 的生命周期更短,并会被先释放。

    open var foo:String? //这个属性允许在 app 内或 app 外重写和访问。在开发框架的时候,会应用到这个访问修饰符。

    operator:特殊符号,用于检查、修改、组合值。
    static:用于定义类方法,在类型本身进行调用。此外还可以定义静态成员。

    class Person  
    {  
        var jobTitle:String?
    
        static func assignRandomName(_ aPerson:Person)  
        {  
            aPerson.jobTitle = "Some random job"  
        }  
    }
    
    let somePerson = Person()  
    Person.assignRandomName(somePerson)  
    //somePerson.jobTitle 的值是 "Some random job"
    

    struct:通用、灵活的结构体,是程序的基础组成部分,并提供了默认初始化方法。与 class 不同,当 struct 在代码中被传递时,是被拷贝的,并不使用引用计数。除此之外,struct 没有下面的这些功能:

    • 使用继承。
    • 运行时的类型转换。
    • 使用析构方法。
    struct Person  
    {  
        var name:String  
        var age:Int  
        var gender:String  
    }
    
    

    typealias:给代码中已经存在的类,取别名。

    typealias JSONDictionary = [String: AnyObject]
    func parseJSON(_ deserializedData:JSONDictionary){}
    

    defer:用于在程序离开当前作用域之前,执行一段代码。

    关闭文件

    func foo() {
      let fileDescriptor = open(url.path, O_EVTONLY)
      defer {
        close(fileDescriptor)
      }
      // use fileDescriptor...
    }
    
    

    加/解锁:下面是 swift 里类似 Objective-C 的 synchronized block 的一种写法,可以使用任何一个 NSObject 作 lock

    func foo() {
      objc_sync_enter(lock)
      defer { 
        objc_sync_exit(lock)
      }
      // do something...
    }
    

    defer的执行时机:
    defer 的执行时机紧接在离开作用域之后,但是是在其他语句之前。这个特性为 defer 带来了一些很“微妙”的使用方式。比如从 0 开始的自增:

    class Foo {
        var num = 0
        func foo() -> Int {
            defer { num += 1 }
            return num
        }
    
        // 没有 `defer` 的话我们可能要这么写
        // func foo() -> Int {
        //    num += 1
        //    return num - 1
        // }
    }
    
    let f = Foo()
    f.foo() // 0
    f.foo() // 1
    f.num   // 2
    

    fallthrough:显式地允许从当前 case 跳转到下一个相邻 case 继续执行代码。

    let box = 1
    
    switch box  
    {  
        case 0:  
        print("Box equals 0")  
        fallthrough  
        case 1:  
        print("Box equals 0 or 1")  
        default:  
        print("Box doesn't equal 0 or 1")  
    }
    

    repeat:在使用循环的判断条件之前,先执行一次循环中的代码。

    repeat  
    {  
        print("Always executes at least once before the condition is considered")  
    }  
    while 1 > 2
    

    where:要求关联类型必须遵守特定协议,或者类型参数和关联类型必须保持一致。也可以用于在 case 中提供额外条件,用于满足控制表达式。

    • 增加判断条件
    for i in 0…3 where i % 2 == 0  
    {  
        print(i) //打印 0 和 2  
    }
    
    • 协议使用where, 只有基类实现了当前协议才能添加扩展。 换个说法, 多个类实现了同一个协议,该语法根据类名分别为这些类添加扩展, 注意是分别(以类名区分)!!!
    protocol SomeProtocol {
        func someMethod()
    }
    
    class A: SomeProtocol {
        let a = 1
    
        func someMethod() {
           print("call someMethod")
        }
    }
    
    class B {
        let a = 2
    }
    
    //基类A继承了SomeProtocol协议才能添加扩展
    extension SomeProtocol where Self: A {
        func showParamA() {
            print(self.a)
        }
    }
    //反例,不符合where条件
    extension SomeProtocol where Self: B {
        func showParamA() {
            print(self.a)
        }
    }
    let objA = A()
    let objB = B()  //类B没实现SomeProtocol, 所有没有协议方法
    objA.showParamA()  //输出1
    

    super:在子类中,暴露父类的方法、属性、下标。

    class Person  
    {  
        func printName()  
        {  
            print("Printing a name. ")  
        }  
    }
    
    class Programmer : Person  
    {  
        override func printName()  
        {  
            super.printName()  
            print("Hello World!")  
        }  
    }
    
    let aDev = Programmer()  
    aDev.printName() //打印 Printing a name. Hello World!
    

    throw:用于在当前上下文,显式抛出 error。
    throws:指明在一个函数、方法、初始化方法中可能会抛出 error。
    //"throws" 表明在调用方法时,需要使用 try,try?,try!
    true:Swift 用于表示布尔值的两个常量值之一,表示为真。

    以#开头的关键字

    *#file:特殊字面量表达式,返回当前代码所在源文件的名称。 *
    #colorLiteral:在 playground 中使用的字面表达式,用于创建颜色选取器,选取后赋值给变量。

    let aColor = #colorLiteral //创建颜色选取器
    

    #imageLiteral:playground 字面量语法,创建图片选取器,选择并返回 UIImage 实例。
    #fileReference:playground 字面量语法,用于创建文件选取器,选取并返回 NSURL 实例。
    **#sourceLocation:行控制语句,可以指定与原先完全不同的行数和源文件名。通常在 Swift 诊断、debug 时使用。
    #column:一种特殊的字面量表达式,用于获取字面量表示式的起始列数。

    class Person  
    {  
        func printInfo()  
        {  
            print("Some person info - on column (#column)")
        }  
    }
    
    let aPerson = Person()  
    aPerson.printInfo() //Some person info - on column 53
    

    #function:特殊字面量表达式,返回函数名称。在方法中,返回方法名。在属性的 getter 或者 setter 中,返回属性名。在特殊的成员中,比如 init 或 subscript 中,返回关键字名称。在文件的最顶层时,返回当前所在模块名称。

    class Person  
    {  
        func printInfo()  
        {  
            print("Some person info - inside function (#function)")
        }  
    }
    
    let aPerson = Person()  
    aPerson.printInfo() //Some person info - inside function printInfo()
    

    #line:特殊字面量表达式,用于获取当前代码的行数。

    class Person  
    {  
        func printInfo()  
        {  
            print("Some person info - on line number (#line)")
        }  
    }
    
    let aPerson = Person()  
    aPerson.printInfo() //Some person info - on line number 5
    

    #selector:用于创建 Objective-C selector 的表达式,可以静态检查方法是否存在,并暴露给 Objective-C。

    //静态检查,确保 doAnObjCMethod 方法存在  
    control.sendAction(#selector(doAnObjCMethod), to: target, forEvent: event)
    

    #available:基于平台参数,通过 if,while,guard 语句的条件,在运行时检查 API 的可用性

    if #available(iOS 10, *)  
    {  
        print("iOS 10 APIs are available")  
    }
    

    convenience
    在 Swift 中,为保证安全性,init 方法只能调用一次,且在 init 完成后,保证所有非 Optional 的属性都已经被初始化。

    每个类都有指定的初始化方法:designated initializer,这些初始化方法是子类必须调用的,为的就是保证父类的属性都初始化完成了。

    而如果不想实现父类的 designated initializer,可以添加 convenience 关键字,自己实现初始化逻辑。
    convenience 初始化不能调用父类的初始化方法,只能调用同一个类中的 designated initializer。
    由于 convenience 初始化不安全,所以 Swift 不允许 convenience initializer 被子类重写,限制其作用范围。

    class People {
    
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    
    

    通过extension给原有的People类增加init方法:

    // 使用convenience增加init方法
    extension People {
        convenience init(smallName: String) {
            self.init(name: smallName)
        }
    }
    

    结下来,Student类继承父类People

    class Student: People {
        var grade: Int
    
        init(name: String, grade: Int) {
            self.grade = grade
            super.init(name: name)
            // 无法调用
            // super.init(smallName: name)
        }
    
        // 可以被重写 
        override init(name: String) {
            grade = 1
            super.init(name: name)
        }
    
        // 无法重写,编译不通过
        override init(smallName: String) {
            grade = 1
            super.init(smallName: smallName)
        }
    }
    

    子类对象调用父类的convenience的init方法:只要在子类中实现重写了父类convenience方法所需要的init方法的话,我们在子类中就可以使用父类的convenience初始化方法了

    class People {
    
        var name: String
    
        init(name: String) {
            self.name = name
        }
    }
    // 使用convenience增加init方法
    extension People {
        convenience init(smallName: String) {
            self.init(name: smallName)
        }
    }
    
    // 子类
    class Teacher: People {
    
        var course: String
    
        init(name: String, course: String) {
            self.course = course
            super.init(name: name)
        }
    
        override init(name: String) {
            self.course = "math"
            super.init(name: name)
        }
    }
    
    // 调用convenience的init方法
    let xiaoming = Teacher(smallName: "xiaoming")
    
    
    • 总结:子类的designated初始化方法必须调用父类的designated方法,以保证父类也完成初始化。

    @dynamic && @objc

    1,@objc
    OC 是基于运行时,遵循了 KVC 和动态派发,而 Swift 为了追求性能,在编译时就已经确定,而不需要在运行时的,在 Swift 类型文件中,为了解决这个问题,需要暴露给 OC 使用的任何地方(类,属性,方法等)的生命前面加上 @objc 修饰符
    如果用 Swift 写的 class 是继承 NSObject 的话, Swift 会默认自动为所有非 private 的类和成员加上@objc

    在Swift中,我们在给button添加点击事件时,对应的点击事件的触发方法就需要用@objc来修饰

    2,@dynamic
    Swift 中的函数可以是静态调用,静态调用会更快。当函数是静态调用的时候,就不能从字符串查找到对应的方法地址了。这样 Swift 跟 Objective-C 交互时,Objective-C 动态查找方法地址,就有可能找不到 Swift 中定义的方法。

    这样就需要在 Swift 中添加一个提示关键字,告诉编译器这个方法是可能被动态调用的,需要将其添加到查找表中。这个就是关键字 dynamic 的作用。

    final

    • final修饰符只能修饰类,表明该类不能被其他类继承,也就是它没资格当父类。
    • final修饰符也可以修饰类中的属性、方法和下标,但前提是该类并没有被final修饰过。
    • final不能修饰结构体和枚举。

    final正确的使用场景 - 权限控制
    也就是说这个类或方法不希望被继承和重写,具体情况如下:

    • 类或者方法的功能确实已经完备了
      这种通常是一些辅助性质的工具类或者方法,特别那种只包含类方法而没有实例方法的类。比如MD5加密类这种,算法都十分固定,我们基本不会再继承和重写。

    • 避免子类继承和修改造成危险
      有些方法如果被子类继承重写会造成破坏性的后果,导致无法正常工作,则需要将其标为final加以保护。

    • 为了让父类中某些代码一定会执行
      父类的方法如果想要其中一些关键代码在继承重写后仍必须执行(比如状态配置、认证等)。我们可以把父类的方法定义成final,同时将内部可以继承的部分剥离出来,供子类继承重写。下面通过一段代码演示:

    class Parent {
       final func method1() {
           //权限验证(必须执行)
           //.....
    
           method2()
    
           //下面是日志记录(必须执行)
           //..........
       }
    
       func method2(){
           //父类的实现
           //......
       }
    }
    
    class Child : Parent {
       //只能重写父类的method2方法,不能重写method1方法
       override func method2() {
           //子类的实现
           //......
       }
    }
    

    mutating
    在Swift中,包含三种类型(type): structure,enumeration,class
    其中structure和enumeration是值类型(value type),class是引用类型(reference type)
    Swift中protocol的功能比OC中强大很多,不仅能再class中实现,同时也适用于struct、enum。但是struct、enum都是值类型,每个值都是有默认的,所以在实例方法中不能改变,因此就要用mutating关键字,这个关键字可以让在此方法中值的改变,会返回到原始结构里边

    protocol Vehicle {
        var numberOfWheels: Int {get}
        var color: UIColor {get set}
        mutating func changeColor()
    }
    
    struct MyCar: Vehicle {
        let numberOfWheels = 4
        var color = UIColor.blue
        mutating func changeColor() {
            color = .red
        }
    }
    

    subscript

    • 下标(subscript)在数组和字典中使用,但是你可以给任何类型(枚举,结构体,类)增加 下标subscript 的功能;
    • subscript的语法类似实例方法、计算属性,其本质就是方法;
    struct Person {
        var age = 0
        var no  = 0
        subscript(index: Int) -> Int {
            set {
                if index == 0 {
                    age = newValue
                } else {
                    no = newValue
                }
            }
            get {
                if index == 0 {
                    return age
                } else {
                    return no
                }
            }
        }
    }
    
    var p = Person()
    p[0] = 10
    p[1] = 20
    
    print(p.age)  // 10
    print(p[0])   // 10
    
    print(p.no)  // 20
    print(p[1])  // 20
    
    • subscript 的返回值类型决定了:
      1.get 方法的返回值类型;
      2.set 方法中 newValue 的类型;
    • subscript 可以接受多个参数,并且是任意类型;
    • subscript 可以没有 set 方法,但必须要有 get 方法;
    • 如果只有 get 方法,可以省略;
    struct Person {
        var age = 30
    
        subscript(index: Int) -> Int {
            if index == 0 {
                return age
            } else {
                return age * 2
            }
        }
    }
    
    var p = Person()
    print(p[0])  // 30
    print(p[1])  // 60
    
    

    nonmutating

    nonmutating关键字,一般配合set使用。如

    protocol Settings {
        subscript(key: String) -> AnyObject? { get nonmutating set }
    }
    

    它告诉编译器不会修改实例内部的值,也就是set时,不会改变任何其他的变量。

    struct Test2 {
    
        var b: Int {
            get {
                return  2
            }
            nonmutating set {
                print("\(newValue)")
            }
        }
    }
    
    let t = Test2()
    t.b = 3
    print(t.b)
    

    unowned && weak

    • weak:弱引用对象的引用计数不会+1, 必须为可选类型变量
      在声明弱引用对象是必须用var关键字, 不能用let.
      因为弱引用变量在没有被强引用的条件下会变为nil, 而let常量在运行的时候不能被改变.
    class XDTest {
        //会报错
        weak let tentacle = Tentacle() //let is a constant! All weak variables MUST be mutable.
    }
    
    • unowned:相当于__unsafe_unretained, 不安全. 必须为非可选类型.
      unowned引用是non-zeroing(非零的), 在ARC销毁内存后,不会被赋为nil, 这表示着当一个对象被销毁时, 它指引的对象不会清零. 也就是说使用unowned引用在某些情况下可能导致dangling pointers(野指针). 所以在访问无主引用的时候,要确保其引用正确,不然会引起内存崩溃.

    throw && rethrow

    • throws的使用很简单,只需要在可能出现异常的函数或者方法后面添加throws。
      经过这个关键字修饰的函数,在调用的时候,需要程序员加上do-catch来调用.
    • rethrows是异常往上传递的关键字。上面说了throws用在可能出现异常的函数或者方法中,而rethrows针对的不是函数或者方法的本身,而是它携带的闭包类型的参数,当它的闭包类型的参数throws的时候,我们要使用rethrows继续将这个异常往上传递, 直到被调用者使用到。这相比throws多一个传递的环节。

    @dynamicMemberLookup
    这个特性中文可以叫动态查找成员。在使用@dynamicMemberLookup标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMember member: String)方法后我们就可以访问到对象不存在的属性。如果访问到的属性不存在,就会调用到实现的 subscript(dynamicMember member: String)方法,key 作为 member 传入这个方法。

    
    @dynamicMemberLookup
    struct Person {
       subscript(dynamicMember member: String) -> String {
           let properties = ["nickname": "Zhuo", "city": "Hangzhou"]
           return properties[member, default: "undefined"]
       }
    }
    
    //执行以下代码
    let p = Person()
    print(p.city)
    print(p.nickname)
    print(p.name)
    
    

    dynamic:这意味着对这个成员的访问是使用 Objective-C运行时进行动态派发的。

    {
    //隐式指明含有 "objc" 属性
    //这对依赖于 Objc-C 黑魔法的库或者框架非常有用
    //比如 KVO、KVC、Swizzling
    dynamic var name:String?
    }

    image

    @autoclosure,他可以让我们的表达式自动封装成一个闭包。

    相关文章

      网友评论

        本文标题:Swift-关键字

        本文链接:https://www.haomeiwen.com/subject/qmjfvltx.html