一、簡(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
}

點(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 {
}