swift的常見(jiàn)關(guān)鍵字與特性

一、簡(jiǎn)介

關(guān)鍵字 指swift已經(jīng)使用的字段,如let var等。
特性 通過(guò)在@符號(hào)后跟一個(gè)特性名稱與參數(shù),來(lái)聲明類、結(jié)構(gòu)體、方法等滿足該特性,如@available @objc等。
本文按照swift中文的教程順序介紹

二、關(guān)鍵字

  • let var

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

let a = 10 
var b = 0
  • typealias

類型別名,給已存在的類型設(shè)置一個(gè)新名稱

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 聲明函數(shù)
return 函數(shù)返回,退出當(dāng)前函數(shù)
true 真 false 假

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

throws 表明函數(shù)會(huì)拋出錯(cuò)誤
throw 拋出錯(cuò)誤,退出當(dāng)前函數(shù)
try 調(diào)用會(huì)拋出錯(cuò)誤的函數(shù)
do catch 處理拋出的錯(cuò)誤

        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

離開(kāi)當(dāng)前代碼塊前執(zhí)行語(yǔ)句,不論是因?yàn)閞etrun或break還是throw拋出錯(cuò)誤

    func test() {
        print("code1")
        defer {
            print("defer")
        }
        print("code2")
        //會(huì)在打印code2之后執(zhí)行defer
    }
  • for in

遍歷

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

循環(huán)執(zhí)行代碼,直至條件為假

        var num = 3
        //while判斷后面條件真假 為真時(shí)執(zhí)行里面代碼
        while num > 0 {
            num -= 1
        }
        
        num = 3
        repeat {
            num -= 1
        } while num > 0
        //會(huì)先執(zhí)行repeat里面代碼 再判斷while后面條件真假,為真則斷續(xù)執(zhí)行repeat里面代碼
  • switch case default where

分支匹配

        let a: Int = 5
        switch a {
        case 0:
            print("0")
            //默認(rèn)自帶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 當(dāng)滿足條件時(shí)前面的代碼才生效,常用于泛型

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 停止當(dāng)前循環(huán),進(jìn)入下一次循環(huán)

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

break 結(jié)束當(dāng)前的控制流語(yǔ)句

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

fallthrough 在switch中,成功匹配條件后將會(huì)自動(dòng)退出switch,使用fallthrough會(huì)導(dǎo)致不退出

        let a = 1
        switch a {
        case 0...2:
            print("0--2")
            //加上fallthrough會(huì)繼續(xù)向下匹配 此處會(huì)打印other
            fallthrough
        default:
            print("other")
        }

return 函數(shù)返回,退出當(dāng)前函數(shù)
throw 拋出錯(cuò)誤,退出當(dāng)前函數(shù)

  • guard else

當(dāng)條件為假,執(zhí)行else里面的代碼,
與if else功能相反,常用于判空,寫(xiě)成一行,更美觀

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

可異變的
一般來(lái)說(shuō),值類型屬性不能被自身的實(shí)例方法修改,加上mutating以允許它修改自身的屬性

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

在函數(shù)內(nèi)改變形參的值

func swapTwoInts(a: inout Int, b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var a = 10
var b = 20
//需要在參數(shù)前面加上&
swapTwoInts(a: &a, b: &b)
//20 10
print(a, b)
  • enum case

枚舉

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

class 類是引用類型 將類實(shí)例賦給變量時(shí),實(shí)際上是變量引用該實(shí)例,引用計(jì)數(shù)加1
struct 結(jié)構(gòu)體是值類型 將結(jié)構(gòu)體實(shí)例賦給變量時(shí),實(shí)際上是新建一個(gè)結(jié)構(gòu)體并復(fù)制原值。當(dāng)修改結(jié)構(gòu)體中的變量的值時(shí),也是新建一個(gè)結(jié)構(gòu)體復(fù)制原值并更改為新值。

class SomeClass {

}  
struct SomeStructure {

}
  • lazy

延遲賦值
相當(dāng)于oc中的懶加載

//只有當(dāng)使用a時(shí)才會(huì)初始化Array
lazy var a = Array(repeating: 0, count: 5)
lazy var a = {
    retrun "abc"
}()

注意:當(dāng)多個(gè)線程同時(shí)訪問(wèn)非初始化的lazy屬性時(shí),無(wú)法保證屬性只初始化一次

  • static class

將屬性和方法變成類型屬性和類型方法
class 只能在類中的函數(shù)前使用,可以被子類重寫(xiě)
static 全類型的函數(shù)和屬性前使用,在類中時(shí)不能被重寫(xiě)

class Test {
    static var a = 1
    
    static func test1() {
        
    }
    
    class func test2() {
        
    }
}

class Test1: Test {
    //可以重寫(xiě)
    override class func test2() {
        
    }
}
  • subscript

下標(biāo)

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 重寫(xiě)屬性或方法
super 調(diào)用父類的屬性與方法
final 表明屬性或方法不能重寫(xiě)

class Test {
    var a = "a"
    final var b = "b"
    
    func test() {
        print("test")
    }
    
    final func test1() {
        print("test1")
    }
}

class Test1: Test {
    //重寫(xiě)a
    override var a: String {
        get {
            return super.a + "123"
        }
        set {
            super.a = newValue
        }
    }
    //重寫(xiě)test
    override func test() {
        super.test()
    }
    //若重寫(xiě)b或test1() 會(huì)報(bào)錯(cuò)
}
  • init convenience deinit required

init 初始化
convenience 便捷初始化 此方法中必須調(diào)用相同的類里的另一個(gè)初始化器
deinit 反初始化 同oc中的dealloc
required 必要初始化 表明所有該類的子類都必須實(shí)現(xiàn)該初始化器

class Test {
    var name = "liu"
    
    required init() {
        
    }
    convenience init(a: String) {
        self.init()
        name = a
    }
    deinit {
        
    }
}

class Test1: Test {
    required init() {
        //重寫(xiě)必要初始化器時(shí) 不需要添加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 類型轉(zhuǎn)換

        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

擴(kuò)展可以向一個(gè)類型添加新的方法,但是不能重寫(xiě)已有的方法,擴(kuò)展只能添加計(jì)算屬性

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 協(xié)議是為類、結(jié)構(gòu)體、或枚舉類型提供的藍(lán)圖,遵循協(xié)議則需要實(shí)現(xiàn)協(xié)議中所有屬性與方法

protocol SomeProtocol {
    //可讀寫(xiě)
    var a: Int { get set }
    //只讀
    var b: Int { get }
    //類型屬性
    static var a: Int { get set }
    //方法
    func test()
    //類型方法
    static func test1()
    //異變方法
    //在為類實(shí)現(xiàn)該方法的時(shí)候不需要寫(xiě)mutating。 mutating關(guān)鍵字只在結(jié)構(gòu)體和枚舉類型中需要書(shū)寫(xiě)。
    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() {
        
    }
    //類中不需要寫(xiě)mutating
    func test2() {
        
    }
    //實(shí)現(xiàn)指定初始化器或便捷初始化器時(shí) 需要添加required
    required init(a: Int) {
        
    }
}

同時(shí)遵循多個(gè)協(xié)議

protocol protocol1 {
    
}
protocol protocol2 {
    
}
//用,表示同時(shí)遵循兩協(xié)議
class Test: protocol1, protocol2 {
    //用&表示返回值或參數(shù)同時(shí)遵循多個(gè)協(xié)議
    func test(a: protocol1 & protocol2) -> protocol1 & protocol2 {
        return self
    }
}

同樣的,可使用is判斷是否遵循協(xié)議 as轉(zhuǎn)換成該協(xié)議

在OC中 協(xié)議有可選屬性與方法,可使用optional實(shí)現(xiàn)

@objc protocol protocol1 {
    @objc optional var a: Int { get set }
}

//a是可選屬性 可以不實(shí)現(xiàn)
class Test: protocol1 {
    
}
  • associatedtype

關(guān)聯(lián)類型
在協(xié)議中 ,若不確定參數(shù)類型,可使用associatedtype來(lái)實(shí)現(xiàn)泛型的功能

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協(xié)議的對(duì)象
    func test1() -> protocol1 {
        return Test()
    }

    //返回不透明類型 
    @available(iOS 13.0.0, *)
    func test2() -> some protocol1 {
        return Test()
    }

關(guān)于不透明類型與協(xié)議類型,可點(diǎn)擊Swift之不透明類型了解

  • weak unowned

弱引用 無(wú)主引用

直接聲明變量

        //弱引用
        weak var weakSelf = self
        //無(wú)主引用
        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
        //此時(shí)不會(huì)循環(huán)引用 因?yàn)閡ser是weak
        b.user = a

無(wú)主引用

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")
        //此時(shí)不會(huì)循環(huán)引用 因?yàn)閡ser是unowned
        a.car = Car(carName: "car", user: a)

當(dāng)導(dǎo)致循環(huán)引用的屬性是可選的,使用weak,非可選時(shí)使用unowned
在可選時(shí)也是可以使用unowned的,但必須保證屬性總會(huì)引用到一個(gè)合法對(duì)象或 nil

在閉包中使用

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

在閉包和捕獲的實(shí)例self總是互相引用并且總是同時(shí)釋放時(shí),因此使用無(wú)主引用。
相反,在被捕獲的引用可能會(huì)變?yōu)?nil 時(shí),則使用弱引用。

  • open public internal fileprivate private

open public 允許實(shí)體被定義模塊中的任意源文件訪問(wèn),同樣可以被另一模塊的源文件通過(guò)導(dǎo)入該定義模塊來(lái)訪問(wèn)。在指定框架的公共接口時(shí),通常使用 open 或 public 訪問(wèn)。
Internal 允許實(shí)體被定義模塊中的任意源文件訪問(wèn),但不能被該模塊之外的任何源文件訪問(wèn)。通常在定義應(yīng)用程序或是框架的內(nèi)部結(jié)構(gòu)時(shí)使用。(默認(rèn))
fileprivate 將實(shí)體的使用限制于當(dāng)前定義源文件中。當(dāng)一些細(xì)節(jié)在整個(gè)文件中使用時(shí),使用fileprivate隱藏特定功能的實(shí)現(xiàn)細(xì)節(jié)。
private 將實(shí)體的使用限制于封閉聲明中。當(dāng)一些細(xì)節(jié)僅在單獨(dú)的聲明中使用時(shí),使用 private 訪問(wèn)隱藏特定功能的實(shí)現(xiàn)細(xì)節(jié)。

open public的區(qū)別
public 只能在其定義模塊中被繼承
open 可以在其定義模塊中被繼承,也可以在任何導(dǎo)入定義模塊的其他模塊中被繼承

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時(shí)是private權(quán)限 get時(shí)是public權(quán)限
    public private(set) var numberOfEdits = 0
}
  • infix prefix postfix operator

中置 前置 后置 運(yùn)算符

struct Point {
    var x: Int
    var y: Int
}

//自定義運(yùn)算符時(shí) 需要使用operator進(jìn)行聲明
infix operator +++
prefix operator +++
postfix operator ***
extension Point {
    //中置運(yùn)算符 結(jié)果為相加
    static func +++(left: Point, right: Point) -> Point {
        return Point(x: left.x + right.x, y: left.y + right.y)
    }
    //前置運(yùn)算符 結(jié)果為+1
    static prefix func +++(point: Point) -> Point {
        return Point(x: point.x + 1, y: point.y + 1)
    }
    //后置運(yùn)算符 結(jié)果為平方
    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

表明閉包是可逃逸的
一般來(lái)說(shuō),函數(shù)參數(shù)的閉包只能在函數(shù)內(nèi)使用,若要將閉包在函數(shù)外也能使用,需要添加@escaping

    var escaping: (() -> Void)?

    func test(a: @escaping () -> Void) {
        self.escaping = a
    }

    test {
        print("escaping")
    }
  • @autoclosure

自動(dòng)閉包
默認(rèn)情況下

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

添加了自動(dòng)閉包后

        var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
        func test(a: @autoclosure () -> String) {
            print(a())
        }
        //此處傳入的時(shí)String類型 @autoclosure的作用就是將String自動(dòng)包裝成() -> String
        test(a: customersInLine.remove(at: 0))

自動(dòng)閉包后,代碼可讀性會(huì)變差,不容易直接看出參數(shù)的類型,建議少用。

  • @propertyWrapper

屬性包裝 將類、結(jié)構(gòu)體、枚舉包裝成一個(gè)屬性wrappedValue

@propertyWrapper
struct Test {
    private var number = 5
    //該值可通過(guò)$調(diào)用
    var projectedValue = 0
    //使用@propertyWrapper 必須實(shí)現(xiàn)計(jì)算屬性wrappedValue
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 10) }
    }
    init() {
        
    }
    init(number: Int) {
        self.number = number
    }
}

class ClassDemo {
    //此處會(huì)調(diào)用init()初始化方法
    @Test var a: Int
    @Test var b: Int
    var c = Test()
    //此處會(huì)調(diào)用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
    }

        //有一個(gè)警告 Result of call to 'test()' is unused
        test()
        //可以用該方式避免警告
        let _ = test()

當(dāng)使用@discardableResult時(shí)

    @discardableResult
    func test() -> Bool {
        return true
    }
    //沒(méi)有警告
    test()
  • @available

可用來(lái)標(biāo)識(shí)計(jì)算屬性、函數(shù)、類、協(xié)議、結(jié)構(gòu)體、枚舉等類型的生命周期。(存儲(chǔ)屬性不可用)

//swift 5.0及以后可用
@available(swift 5.0)
//iOS 10.0及以后可用(*表示其他平臺(tái)可用)
@available(iOS 10.0, *)
//表明不可用,結(jié)構(gòu)體已改成Type
@available(*, unavailable, renamed: "Type")
struct Point {
    var x: Int
    var y: Int
}
struct Type {
    var x: Int
    var y: Int
}

image.png

點(diǎn)Fix后會(huì)將Point替換成Type
對(duì)于不可用的Point 可使用typealias Point = Type在不修改大量錯(cuò)誤的情況繼續(xù)使用Point

//表示該結(jié)構(gòu)體在iOS8.0可用,在iOS10.0廢棄,在iOS12.0已刪除,提示為use Type,已改名為T(mén)ype
@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

為類、結(jié)構(gòu)體、枚舉或者協(xié)議添加這個(gè)特性來(lái)將這個(gè)類型的實(shí)例視為可調(diào)用函數(shù)

@dynamicCallable
struct TelephoneExchange {
    //兩方法必須實(shí)現(xiàn)一個(gè)
    //參數(shù)為數(shù)組
    func dynamicallyCall(withArguments value: [Any]) {
        print(value)
    }
    //參數(shù)為字典
    func dynamicallyCall(withKeywordArguments value: [String: Any]) {
        print(value)
    }
}

        let a = TelephoneExchange()
        //下面兩調(diào)用方式功能一致
        a.dynamicallyCall(withArguments: [1, 2, 3, 4])
        a(1, 2, 3, 4)
        //下面兩調(diào)用方式功能一致
        a.dynamicallyCall(withKeywordArguments: ["a": 1,"b": 2, "c": 3, "d": 4])
        a(a: 1, b: 2, c: 3, d: 4)
  • @dynamicMemberLookup

允許類、結(jié)構(gòu)體、枚舉或者協(xié)議在運(yùn)行時(shí)可通過(guò)名字查找成員

@dynamicMemberLookup
struct DynamicStruct {
    let dictionary = ["a": 5, "b": 10, "c": 15, "d": 22]
    //必須實(shí)現(xiàn)該方法 參數(shù)與返回類型可自定
    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 中表示的聲明上——例如,非內(nèi)嵌類,協(xié)議,非泛型枚舉(原始值類型只能是整數(shù)),類和協(xié)議的屬性、方法(包括 setter 和 getter ),初始化器,反初始化器,下標(biāo)。 objc 特性告訴編譯器,這個(gè)聲明在 Objective-C 代碼中是可用的。nonobjc特性則表示這個(gè)聲明在Objective-C中不可用的

@objc class Test: NSObject {
    
}

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

給任何可以擁有 objc 特性的類聲明添加這個(gè)特性。 objc 特性會(huì)隱式地添加到類的 Objective-C 兼容成員、擴(kuò)展、子類以及它們所有的擴(kuò)展。

@objcMembers class Test {
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容