在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
可选项绑定(Optional Binding)可以使用可选项绑定来判断可选项是否包含值 p如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false if let number = Int("123") { 等价写法 f let first = Int("4") { if let second = Int("42") { if first < second && second < 100 { print("\(first) < \(second) < 100") } } } // 4 < 42 < 100 if let first = Int("4"), let second = Int("42"), first < second && second < 100 { print("\(second) < \(second) < 100") } // 4 < 42 < 100 空合并运算符 ??(Nil-Coalescing Operator) let a: Int? = 1 let b: Int? = 2 let c = a ?? b // c是Int? , Optional(1)
let a: Int? = 1 let b: Int = 2 let c = a ?? b // c是Int , 1
let a: Int? = nil let b: Int = 2 let c = a ?? b // c是Int , 2
let a: Int? = nil let b: Int? = 2 let c = a ?? b // c是Int? , Optional(2)
let a: Int? = nil let b: Int = 2// 如果不使用??运算符 let c: Int if let tmp = a { c = tmp } else { c=b }
let a: Int? = nil let b: Int? = nil let c = a ?? b // c是Int? , nil 多个 ?? 一起使用 let a: Int? = 1 let b: Int? = 2 let c = a ?? b ?? 3 // c是Int , 1 guard语句 当guard语句的条件为false时,就会执行大括号里面的代码 当guard语句的条件为true时,就会跳过guard语句 guard语句特别适合用来“提前退出” 当使用guard语句进行可选项绑定时,绑定的常量(let)、变量(var)也能在外层作用域中使用 func login(_ info: [String : String]) { guard let username = info["username"] else { print("请输入用户名") return } guard let password = info["password"] else { print("请输入密码") return } // if username .... // if password .... print("用户名:\(username)", "密码:\(password)", "登陆ing") } guard 条件 else { 退出当前作用域 // return、break、continue、throw error } 隐式解包(Implicitly Unwrapped Optional)在某些情况下,可选项一旦被设定值之后,就会一直拥有值 在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值 可以在类型后面加个感叹号 ! 定义一个隐式解包的可选项 let num1: Int! = 10 let num2: Int = num1 if num1 != nil { print(num1 + 6) // 16 } if let num3 = num1 { print(num3) } let num1: Int! = nil // Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value let num2: Int = num1
结构体在 Swift 标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分 比如Bool、Int、Double、 String、Array、Dictionary等常见类型都是结构体 所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法) 类类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器 如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器 成员的初始化是在这个初始化器中完成的 class Point { var x: Int = 10 var y: Int = 20 } let p1 = Point() class Point { var x: Int var y: Int init() { x = 10 y = 20 } } let p1 = Point() //上面2段代码是完全等效的 结构体与类的本质区别 结构体是值类型(枚举也是值类型),类是引用类型(指针类型) 值类型 值类型赋值给var、let或者给函数传参,是直接将所有内容拷贝一份 类似于对文件进行copy、paste操作,产生了全新的文件副本。属于深拷贝(deep copy) 引用类型 引用赋值给var、let或者给函数传参,是将内存地址拷贝一份 p类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件。属于浅拷贝(shallow copy) 在Swift中,创建类的实例对象,要向堆空间申请内存
枚举、结构体、类都可以定义方法 一般把定义在枚举、结构体、类内部的函数,叫做方法 方法占用对象的内存么? 不占用, 方法、函数都存放在代码段
闭包(Closure)闭包是可以在你的代码中被传递和引用的功能性独立代码块 全局函数是一个有名字但不会捕获任何值的闭包; 内嵌函数是一个有名字且能从其上层函数捕获值的闭包;
可以把闭包想象成是一个类的实例对象 内存在堆空间 捕获的局部变量\常量就是对象的成员(存储属性) 组成闭包的函数就是类内部定义的方法
尾随闭包 如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包可以增强函数的可读性 尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式 逃逸闭包 当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它是在函数返回之后调用的。 当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写 escaping 来明确闭包是允许逃逸的。
属性存储属性(Stored Property) 类似于成员变量这个概念 存储在实例的内存中 结构体、类可以定义存储属性 ü 枚举不可 在创建类 或 结构体的实例时,必须为所有的存储属性设置一个合适的初始值 可以在初始化器里为存储属性设置一个初始值 可以分配一个默认的属性值作为属性定义的一部分
计算属性(Computed Property) 本质就是方法(函数) 不占用实例的内存 枚举、结构体、类都可以定义计算属性 set传入的新值默认叫做newValue,也可以自定义 只读计算属性:只有get,没有set 定义计算属性只能用var,不能用let let代表常量:值是一成不变的 计算属性的值是可能发生变化的(即使是只读计算属性) 枚举原始值rawValue的本质是:只读计算属性 单例模式 public class FileManager { public static let shared = FileManager() private init() { } } public class FileManager { public static let shared = { // .... // .... r eturn FileManager() }() private init() { } } 初始化器类、结构体、枚举都可以定义初始化器 类有2种初始化器:指定初始化器(designated initializer)、便捷初始化器(convenience initializer) 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器 默认初始化器总是类的指定初始化器 类偏向于少量指定初始化器,一个类通常只有一个指定初始化器
初始化器的相互调用规则 指定初始化器必须从它的直系父类调用指定初始化器 便捷初始化器必须从相同的类里调用另一个初始化器 便捷初始化器最终必须调用一个指定初始化器 // 指定初始化器 init(parameters) { statements } // 便捷初始化器 convenience init(parameters) { statements } 重写 当重写父类的指定初始化器时,必须加上override(即使子类的实现是便捷初始化器) 如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上override 因为父类的便捷初始化器永远不会通过子类直接调用,因此,严格来说,子类无法重写父类的便捷初始化器 1、如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器 如果子类提供了父类所有指定初始化器的实现(要么通过方式1继承,要么重写) required 用required修饰指定初始化器,表明其所有子类都必须实现该初始化器(通过继承或者重写实现) 如果子类重写了required初始化器,也必须加上required,不用加override 属性观察器 父类的属性在它自己的初始化器中赋值不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器
反初始化器(deinit)einit叫做反初始化器,类似于C++的析构函数、OC中的dealloc方法 当类的实例对象被释放内存时,就会调用实例对象的deinit方法 class Person { deinit { print("Person对象销毁了") } } deinit不接受任何参数,不能写小括号,不能自行调用 父类的deinit能被子类继承 子类的deinit实现执行完毕后会调用父类的deinit
协议(Protocol)协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开) 协议中定义属性时必须用var关键字 实现协议时的属性权限要不小于协议中定义的属性权限
协议的继承 一个协议可以继承其他协议 protocol Runnable { func run() } protocol Livable : Runnable { func breath() } class Person : Livable { func breath() {} func run() {} } CaseIterable 让枚举遵守CaseIterable协议,可以实现遍历枚举值 num Season : CaseIterable { case spring, summer, autumn, winter } let seasons = Season.allCases print(seasons.count) // 4 for season in seasons { print(season) } // spring summer autumn winter is、as?、as!、asis用来判断是否为某种类型,as用来做强制类型转换
自定义错误Swift中可以通过Error协议自定义运行时的错误信息 enum SomeError : Error { case illegalArg(String) case outOfBounds(Int, Int) case outOfMemory } 函数内部通过throw抛出自定义Error,可能会抛出Error的函数必须加上throws声明 unc divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArg("0不能作为除数")
}
return num1 / num2
}
需要使用try调用可能会抛出Error的函数 var result = try divide(20, 10) do-catch 可以使用do-catch捕捉Error func test() { print("1") do { print("2") print(try divide(20, 0)) print("3") } catch let SomeError.illegalArg(msg) { print("参数异常:", msg) } catch let SomeError.outOfBounds(size, index) { print("下标越界:", "size=\(size)", "index=\(index)") } catch SomeError.outOfMemory { print("内存溢出") } catch { print("其他错误") } print("4") } try?、try! 可以使用try?、try!调用可能会抛出Error的函数,这样就不用去处理Error assert(断言) 很多编程语言都有断言机制:不符合指定条件就抛出运行时错误,常用于调试(Debug)阶段的条件判断 n 默认情况下,Swift的断言只会在Debug模式下生效,Release模式下会忽略 func divide(_ v1: Int, _ v2: Int) -> Int { assert(v2 != 0, "除数不能为0") return v1 / v2 } print(divide(20, 0)) 泛型(Generics)泛型可以将类型参数化,提高代码复用率,减少代码量 泛型函数赋值给变量 func swapValues<T>(_ a: inout T, _ b: inout T) { (a, b) = (b, a) }
继承(Inheritance)值类型(枚举、结构体)不支持继承,只有类支持继承 没有父类的类,称为:基类 Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类 子类可以重写父类的下标、方法、属性,重写必须加上override关键字 重写属性 子类可以将父类的属性(存储、计算)重写为计算属性 子类不可以将父类属性重写为存储属性 只能重写var属性,不能重写let属性 重写时,属性名、类型要一致 子类重写后的属性权限 不能小于 父类属性的权限 如果父类属性是只读的,那么子类重写后的属性可以是只读的、也可以是可读写的 如果父类属性是可读写的,那么子类重写后的属性也必须是可读写的 重写类型属性 被class修饰的计算类型属性,可以被子类重写 被static修饰的类型属性(存储、计算),不可以被子类重写 可以在子类中为父类属性(除了只读计算属性、let属性)增加属性观察器 final 被final修饰的方法、下标、属性,禁止被重写 被final修饰的类,禁止被继承 访问控制(Access Control)在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列, 实体指被访问级别修饰的内容) open:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上) public:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写 internal:只允许在定义实体的模块中访问,不允许在其他模块中访问 fileprivate:只允许在定义实体的源文件中访问 private:只允许在定义实体的封闭声明中访问 绝大部分实体默认都是internal 级别
Swift调用OC 新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h 在{targetName}-Bridging-Header.h 文件中#import OC需要暴露给Swift的内容 OC调用Swift Xcode已经默认生成一个用于OC调用Swift的头文件,文件名格式是: {targetName}-Swift.h Swift暴露给OC的类最终继承自NSObject 使用@objc修饰需要暴露给OC的成员 使用@objcMembers修饰类 Xcode会根据Swift代码生成对应的OC声明,写入{targetName}-Swift.h 文件
KVC\KVO Swift 支持 KVC \ KVO 的条件 属性所在的类、监听器最终继承自 NSObject 用 @objc dynamic 修饰对应的属性 class Person: NSObject { @objc dynamic var age: Int = 0 var observer: Observer = Observer() override init() { super.init() self.addObserver(observer, forKeyPath: "age", options: .new, context: nil) } deinit { self.removeObserver(observer, forKeyPath: "age") } } var p = Person() // observeValue Optional(20) p.age = 20 // observeValue Optional(25) p.setValue(25, forKey: "age") class Observer: NSObject { override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { print("observeValue", change?[.newKey] as Any) } }
|
请发表评论