参考:
The
Swift Programming Language (6 beta)
SwiftUI
Documentation
SwiftUI
Tutorials
App
Dev Tutorials
Swift 语法
变量与常量
常量: let a: Double = 0
;变量使用 var
;默认类型推断为 Int 和 Double;
Swift 不会做任何隐式类型转换;可以用 "\(name)\"
表示转化成字符串;
类型别名: typealias newname = type
;
内置基本数据类型:Int(64位),UInt(一般不用),Float,Double(64位),Bool,String,Character(同样用双引号);
集合数据类型:Array,Dictionary,Set;
引用类型:Class(Struct 是值类型),Closure,Function;
数组声明:var someInts = [Int](repeating: 0, count: 3)
;可以 append()
,可以用 +
合并;
字典:var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
;
枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 enum Student { case Name (String ) case Mark (Int ,Int ,Int ) } var studMarks = Student .Mark (98 ,97 ,95 )switch studMarks {case .Name (let studName): print ("\(studName) 。" ) case .Mark (let Mark1 , let Mark2 , let Mark3 ): print ("\(Mark1) ,\(Mark2) ,\(Mark3) " ) }
1 2 3 enum Month : Int { case January = 1 , February , March , April , May , June , July , August , September , October , November , December }
Optional 类型
在声明的类型后添加 ?
表示一个 Optional
类型值,当未赋值时,此值将为 nil
;
在声明的类型后添加 !
表示一个 Implicity Unwrapped
Optional (隐式解包可选类型)值,此后取值不再需要加 !
,如果取值时值为 nil
会直接运行错误,适合确定值永远不为
nil
时使用;
在确定不为空时使用 !
强制解包;显式类型转换会返回一个
Optional 已处理转换失败的情况;
可选绑定(Optional
Binding):if let name=optionalName {} else {}
;
guard let name=optionalName else {}
;
合并空值运算符: a ?? b
在 a 有值时展开,a 为
nil
时返回 b;b 表达式返回值必须与 a 中存储值同类型;
可选链: if let a = A.B?.C
;
语句
行末无需分号,但一行多个表达式间需要分号;判断子句无需小括号;
where
子句与 let
配合,多个子句之间用逗号分分隔, if
语句中必须所有子句均满足, switch
子句中只需满足一个子句;
1 if let hello = optionalHello where hello.hasPrefix('H '), let name = optionalName {}
范围(Range): 0..<10
左闭右开,
0...10
全闭;
学习将不需要的循环变量设为 _
;
函数与闭包
函数声明:func doSomething() -> returnType {}
;
调用函数时,第一个参数无需写明形参名,其余参数均需写明,除非函数定义中使用
_
作为 argumentLabel(不指定的话默认使用 parameterName 作为
argumentLabel);
函数签名可以作为类型:
var addition: (Int, Int) -> Int = sum
;
闭包本质是代码块(A chunk of code),函数属于闭包:
1 2 3 4 5 6 7 {(Int , Int ) -> Bool in ... } var reversed = names.sorted( by: { $0 > $1 } )var reversed = names.sorted(by: > )func aFunc (closure : () -> Void ) { ... }
形参 inout:相当于变参,调用时传递 &a;
泛型: func swap<T> (_ a: inout T, _ b: inout T)
,可以在尖括号中添加 T 需要继承的父类或遵循的协议;
关联类型:在协议中使用 associatedtype Item
定义一个
Item 类型,在实现时使用 typealias Item = Int
指定关联类型;
可变参数(Variadic Parameters): Double...
,使用
for-in 遍历;
Trailing Closures:若函数最后一个参数是闭包,则可以写到 () 之后:
aFunc(){ ... }
;
Completion Handler:函数末尾回调,通常使用 Trailing Closures
写;
逃逸闭包(Escaping
Closures):闭包可能在函数返回后被调用,如异步操作或被存储在外部变量中,需要标注
@escaping
;此时闭包会被储存在堆中,所捕获的变量生命周期延长;
Modifier:一类函数,调用时返回另一个实例,从而允许链式调用:a.f1().f2().f3();
类
只能单继承;同名函数必须使用 override
关键字,不使用将编译错误;不允许 overwrite;
lazy
成员属性在第一次使用时才计算器初始值;
构造器(Initializer):
init(name:String) { self.name = name }
;调用构造器时所有参数均需写明形参名;可失败构造器: init?
;析构器: deinit {...}
,没有小括号;
自定义构造器后便不再提供默认构造器,且要求所有属性均被初始化;
使用 super.init(name: name)
调用父类构造函数;
final
可防止 override;便利(
conveience
)构造器指定部分初始值后调用其它构造函数;
if let square = shape as? Square
使用 as?
实现向下转换可选解包,使用 as!
实现强制解包;使用
is
判断是否为某个子类;
使用 [Any]
定义可以存储任意不同引用类型的数组,使用
[AnyObject]
定义可以存储不同类类型的数组;
类比结构体增加了:继承,运行时类型判断,释放被分配的资源,多次引用;
struct 内的方法不能改变其属性,必须声明为 mutating
;而
class 内的方法可以改变其内部属性而无需 mutating
;
协议:类似虚函数 / 接口,定义一系列必须被遵循者实现的方法;使用
get set
定义可读写性;同时拥有父类和遵循协议的类,冒号后先写父类再写协议;函数名前
required
关键字要求遵循者必须 override 它(遵循者也应标明
required
,类似纯虚函数);
计算属性(Calculated
Properties):不直接存储值,每次通过计算得到;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class sample { var no1 = 0.0 , no2 = 0.0 var length = 300.0 , breadth = 150.0 var middle: (Double , Double ) { get { return (length / 2 , breadth / 2 ); } set (axis){ no1 = axis.0 - (length / 2 ) no2 = axis.1 - (breadth / 2 ) } } var counter: Int = 0 { willSet (newTotal){ print ("\(newTotal) " ); } didSet { print (\(counter - oldValue)"); } } }
类型属性(Type Properties): static
属性(Singleton
单例);类型方法(Type Methods): static
方法;
下标脚本(Subscripts):类似 C++ 中重载 []
;可以重载多个,会自动匹配;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct subexample { let decrementer: Int subscript (index : Int ) -> Int { return decrementer / index get { } set (newValue) { } } } let division = subexample(decrementer: 100 )print ("100 除以 9 等于 \(division[9 ]) " )
避免循环引用导致引用计数无法归零:可能为 nil
时使用弱引用( weak
),反之使用无主引用(
unowned
);
通过扩展 extension
给类或协议增加新的属性、方法、构造器等;
Swift
的四种访问控制:public,internal(默认,同模块内可使用),fileprivate(仅当前源文件可使用),private;
属性包装器(Property Wrapper):分离变量定义与存储,通过
propertyWrapper
注解定义变量行为和合法性判断(类似计算属性);
ClassName.self() 代表类类型本身(元类型);
其它
Swift 采用 Unicode 编码,变量名可以为中文或 emoji 等;
Swift 对空格有要求;
Swift 3 后取消了 ++
、 --
操作以及
switch-for(类似 C 中的 for) 循环;
Swift 检查变量使用前初始化、索引检查、整数溢出、Optional 处理
nil
值、内存管理;
Swift 的 /*...*/
注释可以嵌套;
使用 do-throw-catch
异常处理; try?
若抛出错误则返回 nil
, try!
若错误直接崩溃;
some
用于声明隐式返回类型(Opaque Return Types),
some Shape
表示返回一个遵循 Shape
协议的对象,但不暴露具体对象;
Swift 提供指针类型,但不常用;
一些协议:
Encodable 协议确保能转成 JSON 等格式,Decodable 反之,Codable
是二者结合;
Hashable 协议使其能执行相等运算;
Identifiable 使其能被用于创建 Lists 和 ForEach(传入一个 List 和一个
trailing closure,对每个 List 元素执行闭包);
泛型(Generic Types):类似 C++
的泛型;可以规定泛型服从的协议,有点类似 C++20 的 Concept;
SwiftUI
框架
SwiftUI 遵从
MVVM(Model-View-ViewModel)软件架构模式;声明式语言,用户不指定代码的具体实现;响应式更新,在被观察变量被改变后自动更新组件等状态;
原先的 MVC 架构:ViewController 创建 View 并将其显示,或使用
Outlet( @IBOutlet weak var myLabel: UILabel!
)引用
storyboard 或 xib 界面组件,使用 Action(
@IBAction func buttonClicked(_ sender: UIButton) { myLabel.text = "1"; }
) 接收组件消息并控制组件行为;
MVC 架构初始默认文件:
AppDelegate.swift
:应用代理类,用于处理应用的生命周期时间;方法有:启动前调用,进入后台后调用,进入前台前调用;
SceneDelegate.swift
:用于显示和管理用户界面窗口,每个窗口对应一个 UIWindows
;方法有:设置初始 ViewController,场景进入后台后调用;
ViewController.swift
:用于管理用户界面交互;方法有:视图初始化配置,处理用户输入与交互,管理视图生命周期(视图出现前调用,视图消失后调用);
MVVC:
View 负责显示组件并接收用户的交互(使用 Combine Framework);
通过@State
声明视图内部(包括子视图)私有状态(不能是计算属性),一般为基本类型和结构体;
通过 @StateObject
自己创建和管理一个可观测对象(ObservableObject,往往是ViewModel);
通过 @ObservedObject
观察一个外部的遵循 ObservableObject
协议的实例;
通过 @Published
在 ObservableObject
实例中声明需要其它视图响应的变量;
SwiftUI 使用 @State
类型包装器来允许在 Struct
中修改值;使用此注解时,值被移出 struct,移入 SwiftUI
管理的共享存储中;
子视图 View 若想共享 @State
值,可以使用
@Binding
(二者类似 @StateObject
与
@ObservedObject
的关系,一个声明一个使用)实现双向绑定,且声明子视图时在传递此值时前面加
$ 符号;
ViewModel 充当连接 View 与 Model 的 Binder 角色;
ViewModel 的属性使用 @Published
标记以使 View
在这些属性改变时自动刷新;
ViewModel 引用 Model 并处理数据;
特性
@Environment
用于访问 SwiftUI
中由系统或其他视图提供的环境,通常为全局配置或状态,如颜色方案(亮暗模式)、字体、布局方向等。
@EnvironmentObject
用于实现依赖注入,父类可以通过
.environment()
modifier 指定子类使用
@EnvironmentObject
定义的可观测对象;类似传参,但会实时更新和共享(类似传指针);
通过 modifier 增加定义的组件的属性和内容;自定义 View
时需要声明一个继承 View 的 struct,然后声明计算属性 body;
SwiftUI 使用 ViewBuilder
语法糖,通过重载静态方法等技术允许并列声明组件,更接近自然语言的效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct MyView : View { var body: some View { VStack { Text ("Hello, World!" ) if Bool .random() { Text ("This is a random text!" ) } } } } struct MyView : View { var body: some View { VStack { let content = ViewBuilder .buildBlock( Text ("Hello, World!" ), Bool .random() ? ViewBuilder .buildBlock(Text ("This is a random text!" )) : ViewBuilder .buildBlock(EmptyView ()) ) content } } }