美文网首页
swift的常见关键字与特性

swift的常见关键字与特性

作者: 想聽丿伱說衹愛我 | 来源:发表于2020-10-16 14:54 被阅读0次

    一、简介

    关键字 指swift已经使用的字段,如let var等。
    特性 通过在@符号后跟一个特性名称与参数,来声明类、结构体、方法等满足该特性,如@available @objc等。
    本文按照swift中文的教程顺序介绍

    二、关键字

    • let var

    let 常量 有初始值后常量不可改变
    var 变量

    let a = 10 
    var b = 0
    
    • typealias

    类型别名,给已存在的类型设置一个新名称

    typealias Type = Int
    let a: Type = 10
    
    • if else

    如果 否则

        let a: Int = 5
        if a > 10, a < 20 {
            
        } else if a < 0 && a > -20 {
            
        } else {
            
        }
    
        let a: Int?
        //需要a为可选类型
        if let b = a {
                
        } else {
                
        }
    
    • func return true false

    func 声明函数
    return 函数返回,退出当前函数
    true 真 false 假

    func test(a: Int) -> Bool {
        if a%2 == 0 {
            return true
        } else {
            return false
        }
    }
    
    • throws throw try do catch

    throws 表明函数会抛出错误
    throw 抛出错误,退出当前函数
    try 调用会抛出错误的函数
    do catch 处理抛出的错误

            enum TestError: Error {
                case error1
                case error2
            }
            
            func test(a: Int) throws {
                if a < 0 {
                    throw TestError.error1
                } else if a > 10 {
                    throw TestError.error2
                } else {
                    print("pass")
                }
            }
            
            do {
                try test(a: 15)
                print("success")
            } catch TestError.error1 {
                print("error1")
            } catch TestError.error2 {
                print("error2")
            } catch {
                
            }
    
    • defer

    离开当前代码块前执行语句,不论是因为retrun或break还是throw抛出错误

        func test() {
            print("code1")
            defer {
                print("defer")
            }
            print("code2")
            //会在打印code2之后执行defer
        }
    
    • for in

    遍历

            let a = ["a", "b", "c"]
            for item in a {
                print(item)
            }
    
    • while repeat

    循环执行代码,直至条件为假

            var num = 3
            //while判断后面条件真假 为真时执行里面代码
            while num > 0 {
                num -= 1
            }
            
            num = 3
            repeat {
                num -= 1
            } while num > 0
            //会先执行repeat里面代码 再判断while后面条件真假,为真则断续执行repeat里面代码
    
    • switch case default where

    分支匹配

            let a: Int = 5
            switch a {
            case 0:
                print("0")
                //默认自带break的效果,因此break可省略
                break
            case 1, 2, 3:
                print("123")
            case 4..<10:
                print("4--10")
            case let b where b > 10:
                print(b)
            default:
                print("<0")
            }
    

    case 还用于枚举

    enum Type {
        case type1
        case type2
        case type3
    }
    

    where 当满足条件时前面的代码才生效,常用于泛型

    extension Array where Element == Int {
        func sum() -> Int {
            var a = 0
            for item in self {
                a += item
            }
            return a
        }
    }
    
    let a = [1, 2, 3]
    //6
    print(a.sum())
    
    • continue break fallthrough return throw

    continue 停止当前循环,进入下一次循环

            for item in 0...3 {
                if item == 2 {
                    continue//不会打印2
                }
                print(item)
            }
    

    break 结束当前的控制流语句

            for item in 0...3 {
                if item == 2 {
                    break//不会打印2 3
                }
                print(item)
            }
    

    fallthrough 在switch中,成功匹配条件后将会自动退出switch,使用fallthrough会导致不退出

            let a = 1
            switch a {
            case 0...2:
                print("0--2")
                //加上fallthrough会继续向下匹配 此处会打印other
                fallthrough
            default:
                print("other")
            }
    

    return 函数返回,退出当前函数
    throw 抛出错误,退出当前函数

    • guard else

    当条件为假,执行else里面的代码,
    与if else功能相反,常用于判空,写成一行,更美观

        func test(a: String?) -> String? {
            guard  a != nil else { return nil }
            return "hello" + a!
        }
    
    • mutating

    可异变的
    一般来说,值类型属性不能被自身的实例方法修改,加上mutating以允许它修改自身的属性

        struct abc {
            var a = 2
            var b = 3
            
            mutating func test() {
                a = 3
                b = 4
            }
        }
    
    • inout

    在函数内改变形参的值

    func swapTwoInts(a: inout Int, b: inout Int) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    var a = 10
    var b = 20
    //需要在参数前面加上&
    swapTwoInts(a: &a, b: &b)
    //20 10
    print(a, b)
    
    • enum case

    枚举

    enum Type: Int {
        case type1 = 1
        case type2, type3
    }
    
    • class struct

    class 类是引用类型 将类实例赋给变量时,实际上是变量引用该实例,引用计数加1
    struct 结构体是值类型 将结构体实例赋给变量时,实际上是新建一个结构体并复制原值。当修改结构体中的变量的值时,也是新建一个结构体复制原值并更改为新值。

    class SomeClass {
    
    }  
    struct SomeStructure {
    
    }
    
    • lazy

    延迟赋值
    相当于oc中的懒加载

    //只有当使用a时才会初始化Array
    lazy var a = Array(repeating: 0, count: 5)
    lazy var a = {
        retrun "abc"
    }()
    

    注意:当多个线程同时访问非初始化的lazy属性时,无法保证属性只初始化一次

    • static class

    将属性和方法变成类型属性和类型方法
    class 只能在类中的函数前使用,可以被子类重写
    static 全类型的函数和属性前使用,在类中时不能被重写

    class Test {
        static var a = 1
        
        static func test1() {
            
        }
        
        class func test2() {
            
        }
    }
    
    class Test1: Test {
        //可以重写
        override class func test2() {
            
        }
    }
    
    • subscript

    下标

    class Test {
        var a = ["a", "b", "c"]
        subscript(index: Int) -> String? {
            get {
                if a.count > index {
                    return a[index]
                } else {
                    return nil
                }
            }
            set(newValue) {
                if a.count > index {
                    a[index] = newValue ?? ""
                }
            }
        }
    }
    
            let a = Test()
            //b empty
            print(a[1] ?? "empty", a[4] ?? "empty")
            a[1] = "d"
            a[4] = "e"
            //d empty
            print(a[1] ?? "empty", a[4] ?? "empty")
    
    • override super final

    override 重写属性或方法
    super 调用父类的属性与方法
    final 表明属性或方法不能重写

    class Test {
        var a = "a"
        final var b = "b"
        
        func test() {
            print("test")
        }
        
        final func test1() {
            print("test1")
        }
    }
    
    class Test1: Test {
        //重写a
        override var a: String {
            get {
                return super.a + "123"
            }
            set {
                super.a = newValue
            }
        }
        //重写test
        override func test() {
            super.test()
        }
        //若重写b或test1() 会报错
    }
    
    • init convenience deinit required

    init 初始化
    convenience 便捷初始化 此方法中必须调用相同的类里的另一个初始化器
    deinit 反初始化 同oc中的dealloc
    required 必要初始化 表明所有该类的子类都必须实现该初始化器

    class Test {
        var name = "liu"
        
        required init() {
            
        }
        convenience init(a: String) {
            self.init()
            name = a
        }
        deinit {
            
        }
    }
    
    class Test1: Test {
        required init() {
            //重写必要初始化器时 不需要添加override
        }
    }
    
    • is as

    is 类型判断

            let a: [Any] = [1 ,2 ,3 ,"a", "b", "c", "d", [1], ["key": "value"]]
            var intNum = 0
            var stringNum = 0
            var otherNum = 0
            for item in a {
                if item is Int {
                    intNum += 1
                } else if item is String {
                    stringNum += 1
                } else {
                    otherNum += 1
                }
            }
            //3 4 2
            print(intNum, stringNum, otherNum)
    

    as 类型转换

            let a: [Any] = [1 ,2 ,3 ,"a", "b", "c", "d", [1], ["key": "value"]]
            var sum = 0
            var string = ""
            for item in a {
                if let ele = item as? Int {
                    sum += ele
                } else if let ele = item as? String {
                    string += ele
                } else {
                    
                }
            }
            //6 abcd
            print(sum, string)
    
    • extension

    扩展可以向一个类型添加新的方法,但是不能重写已有的方法,扩展只能添加计算属性

    extension Double {
        var km: Double { return self * 1_000.0 }
        var m: Double { return self }
        var cm: Double { return self / 100.0 }
        var mm: Double { return self / 1_000.0 }
        var ft: Double { return self / 3.28084 }
    }
    
    • protocol optional

    protocol 协议是为类、结构体、或枚举类型提供的蓝图,遵循协议则需要实现协议中所有属性与方法

    protocol SomeProtocol {
        //可读写
        var a: Int { get set }
        //只读
        var b: Int { get }
        //类型属性
        static var a: Int { get set }
        //方法
        func test()
        //类型方法
        static func test1()
        //异变方法
        //在为类实现该方法的时候不需要写mutating。 mutating关键字只在结构体和枚举类型中需要书写。
        mutating func test2()
        //初始化方法
        init(a: Int)
    }
    
    class Test: SomeProtocol {
        var a: Int = 0
        var b: Int = 0
        static var a: Int = 5
        
        func test() {
            
        }
        static func test1() {
            
        }
        //类中不需要写mutating
        func test2() {
            
        }
        //实现指定初始化器或便捷初始化器时 需要添加required
        required init(a: Int) {
            
        }
    }
    

    同时遵循多个协议

    protocol protocol1 {
        
    }
    protocol protocol2 {
        
    }
    //用,表示同时遵循两协议
    class Test: protocol1, protocol2 {
        //用&表示返回值或参数同时遵循多个协议
        func test(a: protocol1 & protocol2) -> protocol1 & protocol2 {
            return self
        }
    }
    

    同样的,可使用is判断是否遵循协议 as转换成该协议

    在OC中 协议有可选属性与方法,可使用optional实现

    @objc protocol protocol1 {
        @objc optional var a: Int { get set }
    }
    
    //a是可选属性 可以不实现
    class Test: protocol1 {
        
    }
    
    • associatedtype

    关联类型
    在协议中 ,若不确定参数类型,可使用associatedtype来实现泛型的功能

    protocol Container {
        associatedtype ItemType
        var a: ItemType { get set }
        func test(a: ItemType)
    }
    
    class Test: Container {
        typealias ItemType = Int
        var a: Int = 0
        func test(a: Int) {
            
        }
    }
    
    • some

    不透明类型

    protocol protocol1 {
        
    }
    
    class Test: protocol1 {
        
    }
    
        //返回遵循protocol1协议的对象
        func test1() -> protocol1 {
            return Test()
        }
    
        //返回不透明类型 
        @available(iOS 13.0.0, *)
        func test2() -> some protocol1 {
            return Test()
        }
    

    关于不透明类型与协议类型,可点击Swift之不透明类型了解

    • weak unowned

    弱引用 无主引用

    直接声明变量

            //弱引用
            weak var weakSelf = self
            //无主引用
            unowned var unownedSelf = self
    

    弱引用

    class Person {
        var name: String
        var car: Car?
        
        init(name: String) {
            self.name = name
        }
    }
    
    class Car {
        var carName: String
        weak var user: Person?
        
        init(carName: String) {
            self.carName = carName
        }
    }
    
            let a = Person(name: "person")
            let b = Car(carName: "car")
            a.car = b
            //此时不会循环引用 因为user是weak
            b.user = a
    

    无主引用

    class Person {
        var name: String
        var car: Car?
        
        init(name: String) {
            self.name = name
        }
    }
    
    class Car {
        var carName: String
        unowned var user: Person
        
        init(carName: String, user: Person) {
            self.carName = carName
            self.user = user
        }
    }
    
             let a = Person(name: "person")
            //此时不会循环引用 因为user是unowned
            a.car = Car(carName: "car", user: a)
    

    当导致循环引用的属性是可选的,使用weak,非可选时使用unowned
    在可选时也是可以使用unowned的,但必须保证属性总会引用到一个合法对象或 nil

    在闭包中使用

        var a = "abc"
        
        lazy var someClosure: () -> String = {
            [unowned self] in
            return self.a + "123"
        }
    

    在闭包和捕获的实例self总是互相引用并且总是同时释放时,因此使用无主引用。
    相反,在被捕获的引用可能会变为 nil 时,则使用弱引用。

    • open public internal fileprivate private

    open public 允许实体被定义模块中的任意源文件访问,同样可以被另一模块的源文件通过导入该定义模块来访问。在指定框架的公共接口时,通常使用 open 或 public 访问。
    Internal 允许实体被定义模块中的任意源文件访问,但不能被该模块之外的任何源文件访问。通常在定义应用程序或是框架的内部结构时使用。(默认)
    fileprivate 将实体的使用限制于当前定义源文件中。当一些细节在整个文件中使用时,使用fileprivate隐藏特定功能的实现细节。
    private 将实体的使用限制于封闭声明中。当一些细节仅在单独的声明中使用时,使用 private 访问隐藏特定功能的实现细节。

    open public的区别
    public 只能在其定义模块中被继承
    open 可以在其定义模块中被继承,也可以在任何导入定义模块的其他模块中被继承

    class Test {
        public class A {}
        internal class B {}
        fileprivate class C {}
        private class D {}
         
        public var a = 0
        internal let b = 0
        fileprivate func c() {}
        private func d() {}
        
        //set时是private权限 get时是public权限
        public private(set) var numberOfEdits = 0
    }
    
    • infix prefix postfix operator

    中置 前置 后置 运算符

    struct Point {
        var x: Int
        var y: Int
    }
    
    //自定义运算符时 需要使用operator进行声明
    infix operator +++
    prefix operator +++
    postfix operator ***
    extension Point {
        //中置运算符 结果为相加
        static func +++(left: Point, right: Point) -> Point {
            return Point(x: left.x + right.x, y: left.y + right.y)
        }
        //前置运算符 结果为+1
        static prefix func +++(point: Point) -> Point {
            return Point(x: point.x + 1, y: point.y + 1)
        }
        //后置运算符 结果为平方
        static postfix func ***(point: Point) -> Point {
            return Point(x: point.x * point.x, y: point.y * point.x)
        }
    }
    
            let a = Point(x: 2, y: 3)
            let b = Point(x: 3, y: 5)
            let c = a +++ b
            print(c.x, c.y)//5 8
            let d = +++a
            print(d.x, d.y)//3 4
            let e = a***
            print(e.x, e.y)//4 6
    

    三、特性

    • @escaping

    表明闭包是可逃逸的
    一般来说,函数参数的闭包只能在函数内使用,若要将闭包在函数外也能使用,需要添加@escaping

        var escaping: (() -> Void)?
    
        func test(a: @escaping () -> Void) {
            self.escaping = a
        }
    
        test {
            print("escaping")
        }
    
    • @autoclosure

    自动闭包
    默认情况下

            var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
            func test(a: () -> String) {
                print(a())
            }
            //此处的闭包为{customersInLine.remove(at: 0)} 类型为() -> String
            test(a: {customersInLine.remove(at: 0)})
    

    添加了自动闭包后

            var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
            func test(a: @autoclosure () -> String) {
                print(a())
            }
            //此处传入的时String类型 @autoclosure的作用就是将String自动包装成() -> String
            test(a: customersInLine.remove(at: 0))
    

    自动闭包后,代码可读性会变差,不容易直接看出参数的类型,建议少用。

    • @propertyWrapper

    属性包装 将类、结构体、枚举包装成一个属性wrappedValue

    @propertyWrapper
    struct Test {
        private var number = 5
        //该值可通过$调用
        var projectedValue = 0
        //使用@propertyWrapper 必须实现计算属性wrappedValue
        var wrappedValue: Int {
            get { return number }
            set { number = min(newValue, 10) }
        }
        init() {
            
        }
        init(number: Int) {
            self.number = number
        }
    }
    
    class ClassDemo {
        //此处会调用init()初始化方法
        @Test var a: Int
        @Test var b: Int
        var c = Test()
        //此处会调用init(number:)初始化方法
        @Test(number: 7) var d: Int
    }
    
            let demo = ClassDemo()
            //5 5 5
            print(demo.a, demo.b, demo.c.wrappedValue)
            demo.a = 6
            demo.b = 15
            demo.c.wrappedValue = 20
            //6 10 10
            print(demo.a, demo.b, demo.c.wrappedValue)
            //7
            print(demo.d)
            //0
            print(demo.$a)//此处返回的是projectedValue的值
    
    • @discardableResult

    可忽略返回值

        func test() -> Bool {
            return true
        }
    
            //有一个警告 Result of call to 'test()' is unused
            test()
            //可以用该方式避免警告
            let _ = test()
    

    当使用@discardableResult时

        @discardableResult
        func test() -> Bool {
            return true
        }
        //没有警告
        test()
    
    • @available

    可用来标识计算属性、函数、类、协议、结构体、枚举等类型的生命周期。(存储属性不可用)

    //swift 5.0及以后可用
    @available(swift 5.0)
    //iOS 10.0及以后可用(*表示其他平台可用)
    @available(iOS 10.0, *)
    
    //表明不可用,结构体已改成Type
    @available(*, unavailable, renamed: "Type")
    struct Point {
        var x: Int
        var y: Int
    }
    struct Type {
        var x: Int
        var y: Int
    }
    
    image.png
    点Fix后会将Point替换成Type
    对于不可用的Point 可使用typealias Point = Type在不修改大量错误的情况继续使用Point
    //表示该结构体在iOS8.0可用,在iOS10.0废弃,在iOS12.0已删除,提示为use Type,已改名为Type
    @available(iOS, introduced: 8.0, deprecated: 10.0, obsoleted: 12.0, message: "use Type", renamed: "Type")
    struct Point {
        var x: Int
        var y: Int
    }
    struct Type {
        var x: Int
        var y: Int
    }
    
    • @dynamicCallable

    为类、结构体、枚举或者协议添加这个特性来将这个类型的实例视为可调用函数

    @dynamicCallable
    struct TelephoneExchange {
        //两方法必须实现一个
        //参数为数组
        func dynamicallyCall(withArguments value: [Any]) {
            print(value)
        }
        //参数为字典
        func dynamicallyCall(withKeywordArguments value: [String: Any]) {
            print(value)
        }
    }
    
            let a = TelephoneExchange()
            //下面两调用方式功能一致
            a.dynamicallyCall(withArguments: [1, 2, 3, 4])
            a(1, 2, 3, 4)
            //下面两调用方式功能一致
            a.dynamicallyCall(withKeywordArguments: ["a": 1,"b": 2, "c": 3, "d": 4])
            a(a: 1, b: 2, c: 3, d: 4)
    
    • @dynamicMemberLookup

    允许类、结构体、枚举或者协议在运行时可通过名字查找成员

    @dynamicMemberLookup
    struct DynamicStruct {
        let dictionary = ["a": 5, "b": 10, "c": 15, "d": 22]
        //必须实现该方法 参数与返回类型可自定
        subscript(dynamicMember member: String) -> Int {
            return dictionary[member] ?? 0
        }
    }
    
            let s = DynamicStruct()
            print(s.b)//10
            print(s.z)//0
            print(s[dynamicMember: "b"])//10
            //s.b 效果与 s[dynamicMember: "b"]相同
    
    • @objc @nonobjc

    objc可以在 Objective-C 中表示的声明上——例如,非内嵌类,协议,非泛型枚举(原始值类型只能是整数),类和协议的属性、方法(包括 setter 和 getter ),初始化器,反初始化器,下标。 objc 特性告诉编译器,这个声明在 Objective-C 代码中是可用的。nonobjc特性则表示这个声明在Objective-C中不可用的

    @objc class Test: NSObject {
        
    }
    
    class Test1 {
        @objc var a = "a"
        
        @objc(getPhonebyName:)
        func getPhone(byName name: String) -> Int {
            return 13100110011
        }
    }
    
    • objcMembers

    给任何可以拥有 objc 特性的类声明添加这个特性。 objc 特性会隐式地添加到类的 Objective-C 兼容成员、扩展、子类以及它们所有的扩展。

    @objcMembers class Test {
        
    }
    

    相关文章

      网友评论

          本文标题:swift的常见关键字与特性

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