一、简介
关键字 指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
}
点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 {
}