Swift 通过采用现代编程模式来避免大量常见编程错误:
变量始终在使用前初始化。
检查数组索引超出范围的错误。
检查整数是否溢出。
可选值确保明确处理
nil
值。内存被自动管理。
错误处理允许从意外故障控制恢复。
Swift 5.3 写的项目可以依赖用 Swift 4.2 或 Swift 4 写的项目,反之亦然。这意味着,如果你将一个大的项目分解成多个框架(framework),你可以逐个地将框架从 Swift 4 代码迁移到 Swift 5.3。
Swift 中的 main 函数
/*
在Swift中这句代码就是一个完整的程序
全局作用域的代码会自动当做程序入口,所以Swift项目中不需要写main函数,也不需要;结尾
*/
print("Hello World")
变量类型的确定
- 声明变量时直接初始化,那么系统会做自动类型推导
- 如果声明变量时没有初始化,那么需要指定类型
- 变量一旦声明,那么永远不会被隐式的转换为其他类型,需要显示转换
let carBrand = "BMW"
let carNum = 10086
let carInfo = carBrand + String(carNum)
var carOwner = """
name = Jay;
car = \(carInfo);
"""
数组和字典的初始化
// 数组
var arr = [Int]()
// 字典
var dic = [Int:Int]()
控制流
使用
if
和switch
来进行条件操作,使用for-in
、while
和repeat-while
来进行循环。
-
if
条件必须是Bool表达式,不会隐形的于0做对比
-
可以和 if - let 来处理(可选值类型)值丢失的情况
if let content = opString { print(content) }else { print("可选值类型变量 opString 为空") }
-
switch
- 匹配到case会退出,不需要加break
let abc = "abc" let verify = "abc" // 支持任意类型(不仅仅整数), 各种比较(不仅仅测试相等) switch abc { case "ab": print("直接匹配") case "bc","bcd": print("匹配多个条件") case var verify where verify.hasPrefix("a"): print("verify转换为var") print("先验证verify是否包含指定前缀,再与abc匹配") default: print("不匹配") }
-
for-in
//[String : [Any]] let info = [ "name":["li","zhao","hua"], "age":[1,2,3], ] for dic in info { print ("key = \(dic.key)") let arr = dic.value // unit 是 Any类型 for unit in arr { print(unit) } }
repeat-while
判断条件前会先执行一次代码块内的代码
函数和闭包
- 函数返回值可以是元组生成的复合值
func doSome(num1:Int,num2:Int)->(sum:Int,max:Int){
return (num1 + num2,max(num1, num2))
}
let value = doSome(num1: 1, num2: 2)
print("sum = \(value.sum),max = \(value.max)")
- 函数是第一等类型,可以作为参数、返回值,函数间可以嵌套,内部嵌套的函数可以调用外部的参数
// 内嵌的函数作用域只在当前作用域
func run(name:String){
func eat(){
print("\(name) eat")
}
eat()
}
// 参数和返回值都为函数的函数
func executeParama(_:String){
print("parama function")
}
func executeReturn(_:String)->String {
return "return function";
}
func doNext(before: (String)->Void)->((String)->String) {
before("")
return executeReturn
}
let returnFunction = doNext(before: executeParama)
returnFunction("go")
- 闭包
let nums = [1,2,3]
// map 创建了一个匿名闭包
let new = nums.map {
// 通过 in 分离 参数-返回值类型 、函数体
(num) -> Int in
return num * 3;
}
let new2 = nums.map {
//闭包的极简模式,$0 = 第一个参数,单行函数体,将作为返回值返回
$0 * 3
}
func paramaBlock (num:Int,block:(Int)->Void){
block(num)
}
// 闭包作为最后一个参数时,可以在函数调用()后面直接接大括号初始化这个block
paramaBlock(num: 3) { (num) in
print("another parama num = \(num)")
}
类和对象
- 存储属性(willSet/didSet),计算属性(setter/getter)
// 可以忽略父类
class Person {
// 无默认值的存储属性需要在 init中 初始化
var name:String
// 可选值类型的属性不需要默认值
var car:String?
// 存储属性,可以通过 willSet 来监听值改变
var money : Double {
willSet (value) {
// 默认值为 newValue,可以自定义这个常量名
print("money = \(value)")
}
}
// 计算属性,通过实现set / get 方法来使用
var makeMoney : Double {
set {
money = newValue
}
get {
return money
}
}
init(name:String) {
// 通过 self 区分参数和属性
self.name = name
money = 0
}
}
class Student:Person {
var studentNum:Int
init(name: String,studentNum:Int) {
/*
1.初始化子类存储属性
2.super.init
3.给父类属性赋值
*/
self.studentNum = studentNum
super.init(name: name)
money = 10
}
}
可选值解包
var name:String?
// 通过?解包,如果可选值为nil,那么直接返回nil,后面不执行
name?.append("Jay")
枚举
- 枚举值类型
- 枚举成员设置关联值
// 枚举需要明确类型,如果是String类型,枚举成员的默认值就是该成员转字符串
enum Rank:Int{
// 默认是从0开始,可以显示的指定起始值
case one = 1
case two,three,four,five,six,seven
case king,ace,Unknown
// 枚举可以包含方法
func simpleDescription() -> String {
switch self {
case .one:
return "one"
default:
return String(self.rawValue)
}
}
}
// 通过原始值创建一个 枚举实例
let rank:Rank = Rank.init(rawValue: 11) ?? Rank.Unknown
let str = rank.simpleDescription()
print("output = \(str)") // output = 10
enum Result {
// 给枚举成员设置关联值
case succese(Int,String)
case fail(Int,String)
}
let result = Result.succese(200, "请求成功")
switch result{
case .succese(200, "请求中"):
break
case .succese(200, "请求成功"):
print("满足条件")
break
default:
break
}
结构体
- 结构体和类的最大区别在于,结构体是值传值,类是地址传值
协议
// 枚举、结构体、类都可以遵守协议
protocol RunAble {
// 此处只能进行声明
var speed : Int {get}
//mutating 对枚举和结构体这种值类型起作用,在方法内部可以修改 枚举、结构体对象的值
mutating func run()
}
struct People:RunAble {
var speed: Int = 10
mutating func run() {
speed = 0
}
func eat(){
}
}
// 明确对象的协议类型后,那么对象就只能调用协议内的属性和方法,即便实际执行对象有别的方法,也不能调用
let protocolValue: RunAble = people
print(protocolValue.run)
// print(protocolValue.eat) // 去掉注释可以看到错误
延展
- 使用
extension
来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展让某个在别处声明的类型来遵守某个协议,这同样适用于从外部库或者框架引入的类型。
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
错误处理
// 1.先定义一个继承于 Error的错误枚举
enum AppError : Error {
case noKey
case noName
}
// 2.函数内部要使用 thorw抛出错误,那么要在参数后面加throws,根据调用函数的方式确定是否有返回值
func testError(code:Int) throws -> Int {
if(code != 200){
throw AppError.noKey
}
return 3
}
// 3.1 直接解包调用 ?或者 !
let result = try? testError(code: 300)
// 3.2 通过do-catch捕获错误,不需要解包,如果throw,那么不会走do里面的代码
do {
let abc = try testError(code: 300)
print(abc)
} catch AppError.noKey {
print("no key")
} catch AppError.noName {
print("no name")
}catch {
print("default")
}
defer 代码块
- 函数返回前最后执行
func testDefer (){
defer {
print("函数内的defer");
}
if(true){
defer {
print("if 中的 defer")
}
print("if 中的print")
}
if(true){
return
}
print("函数结尾")
}
泛型
-
函数 + 泛型 , 尖括号
func repeatObj<T>(repeat item:T,count:Int)->[T]{ var results:[T] = [] for _ in 0 ..< count { results.append(item) } return results } // 进阶:取交集 func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Element] where T.Element: Equatable, T.Element == U.Element { var result:[T.Element] = [] for lhsItem in lhs { for rhsItem in rhs { if lhsItem == rhsItem { result.append(lhsItem) } } } return result } let result = anyCommonElements(["A", "B", "C"], ["C"]) let result2 = anyCommonElements([1, 2, 3], [3])
-
枚举 + 泛型
enum OptionalValue<T> { case none // 枚举成员关联值,关联值类型是个泛型 case some(T) } let p1 = OptionalValue.some(1) let p2 = OptionalValue.some("jay") switch p2 { case .some("luc"): print("luc") case .some("jay"): print("jay") default: print("default") break }