swift的常见关键字与特性

一、简介

关键字 指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的常见关键字与特性)