Swift Share
前段时间在公司内部做了一个Swift分享,内容是官方文档的最后五章,包括扩展、协议、泛型、访问控制以及高级运算符,主要内容是扩展和协议,后三章一笔带过~
自己也是正在学习Swift,也算是记录下自己的学习经历。
1. Extensions 扩展
扩展 就是为一个已有的类、结构体、枚举类型或者协议类型添加新功能,与OC中的Category类似。
可以扩展的功能:
- 为已有类型添加新的实例方法和类型方法
class Person {
var name = "Jack"
func work() {
}
}
extension Person {
func age() { // ✅可以添加新方法
}
func work() { // ❌不可以重写方法
}
}
- 为已有类型添加新的便利构造器
class Size {
var width: CGFloat = 0.0
var height: CGFloat = 0.0
init(width: CGFloat, height: CGFloat) {
self.width = width
self.height = height
}
}
extension Size {
convenience init(point: CGSize) {
self.init(width: point.width, height: point.height)
}
}
- 为已有类型添加新下标
// 为Int扩展下标
extension Int {
subscript(_ digtalNumber: Int) -> Int {
return digtalNumber
}
}
29476[3] // 这里为 3
- 为已有类型扩展计算型属性
extension Double {
var m: Double { return self }
var cm: Double { return self * 100.0 }
var mm: Double { return self * 1000.0 }
var invalid: Double // ❌不可以添加存储属性
}
- 为已有类、结构体、枚举添加新的嵌套类型
extension Int {
enum Kind {
//正数 负数 0
case negative, zero, positive
}
var kind:Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
let num1 = 3
if num1.kind == .positive {
print("\(num1) am positive")
}
- 通过扩展遵循协议
protocol RandomGenerator {
func randomBool() -> Bool
}
extension SomeClass: RandomGenerator {
func randomBool() -> Bool {
return true
}
}
- 为已有协议添加默认实现和新的方法实现
protocol RandomGenerator {
func randomBool() -> Bool
}
extension RandomGenerator {
func randomBool() -> Bool {
return true
}
func randomInt() -> Int {
return 0
}
}
2.Protocols 协议
协议定义一个规则去实现特定功能.类 结构体 枚举都可以遵守这个协议,并为这个协议的规则提供具体实现。
Apple在WWDC2015上提出一种编程范式POP(Protocol Oriented Programming),面向协议编程。在Swift标准库中有50多种复杂不一的协议,基本上串起了整个Swift,感觉还是要项目中多实践,不然还是会沦落为语法糖,而不是一种真正的面向协议编程的思想。
在WWDC2015的Session 408可以找到POP的回放。
遵循协议
声明一个协议
protocol SomeProtocol {
}
- 自定义类、结构体遵循协议
struct SomeStruct: SomeProtocol, AnotherProtocol {
}
class SomeClass: SomeProtocol, AnotherProtocol {
}
- 通过扩展遵循协议
extension SomeClass: SomeProtocol {
}
协议要求
方法属性
协议中的属性可以是实例属性也可以是类型属性,协议中的属性只能指定名称和类型以及可读可写。方法要求也和属性类似,可以加 mutating
关键字。
protocol SomeProtocl2 {
var mustBeSettable: Int { get set } // 可读可写
var onlyRead: Int { get } // 只读
static var someTypeProperty: Int { get set } // 类属性前面加static
func method() -> Void
static func classMethod() -> Void
mutating func toggle() -> Void
}
如果有遵循协议的话,协议中的属性就必须要实现,所以如果不想实现就要用到可选型协议。
可选协议
在Swift3中,想实现协议中的方法可选,有两种方式:
- 协议前加
@objc
关键字,方法和属性前也要加@objc
关键字 使用到可选方法或者属性时,它们的类型会自动变为可选类型
@objc protocol CounterDataSource {
@objc optional var fixAdd: Int { get }
@objc optional func addForCount(count: Int) -> Int
}
但是这种方法有几个缺点:
- 只能被OC类或者带
@objc
关键字的类遵循,结构体和枚举都不能遵循- 用到
@objc
关键字就免不了要与OC兼容,所以不推荐这种写法
- 协议和方法都不加关键字,使用 Extension 扩展协议方法的默认实现。为了避免过多不必要的Objective-C兼容,一般推荐这种方法。
protocol CounterDataSource {
var fixAdd: Int { get } // optional
func addForCount(count: Int) -> Int // optional
func degreeCount(count: Int) -> Int // required
}
extension CounterDataSource {
// 想要 fixAdd 和 addForCount(count: Int) 成为 optional 就对其进行扩展
var fixAdd: Int {
// 这里对属性的getter方法进行实现
}
func addForCount(count: Int) -> Int {
// 这里对方法进行实现
}
}
class SomeClass: CounterDataSource {
// 这里只需要实现 degreeCount(count: Int) 这一个 required 方法即可
func degreeCount(count: Int) -> Int {
// 这里对方法进行实现
}
}
构造器要求
构造方法需要遵循协议的构造器必须在方法前加上 required
关键字,来确保所有子类都要实现此构造器,如果是final
修饰的类则不需要。
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
}
}
使用协议实现委托代理
委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。
使用协议同样可以完成委托代理,和OC食用方法类似
protocol SliderDelegate {
func didDragSlider()
}
class Slider {
var delegate: SliderDelegate?
func drag() -> Void {
// ....
delegate?.didDragSlider()
}
}
class ViewController: UIViewController, SliderDelegate {
override func viewDidLoad() {
let slider = Slider()
slider.delegate = self
slider.drag()
}
func didDragSlider() {
print("didDragSlider ======= ")
}
}
我们在OC中会在声明中将delegate定义为weak
,这样在delegate实际对象被释放时会被重置为nil,避免访问已被回收内存导致的crash。在Swift中想对delegate使用weak
需要把protocol限制在类中,一种做法是将protocol声明为Objective-C的,在协议前加@objc
关键字,因为OC的协议只有类能实现,还有一种方法就是下面说的类类型专属协议。
类类型专属协议
通过添加 class
关键字来限制协议只能被类类型遵循,而结构体或枚举不能遵循该协议。
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
}
回头看上面的委托代理,想使用weak
修饰delegate,只要把协议设置成类类型专属协议就可以了。
protocol SliderDelegate: class {
func didDragSlider()
}
class Slider {
weak var delegate: SliderDelegate?
func drag() -> Void {
// ....
delegate?.didDragSlider()
}
}
协议作为类型使用
协议可以像其他普通类型一样使用:
- 作为函数、方法或者构造器中的参数类型或返回值类型
- 作为变量常量或属性的类型
- 作数组或其他容器中元素的类型
协议的继承
协议能够继承一个或多个其他协议,可以在继承的基础上增加新的要求。
当一个类遵循一个协议时,不仅要满足该协议的所有要求,同时也要满足其父类协议的要求。
面向协议编程与 Cocoa 的邂逅 - OneV's Den
3. Generics 泛型
泛型可以避免为类似的功能书写重复的代码,泛型参数可以看成是真实类型的占位符,当泛型被使用到时才会被真实类型替换。Swift中的Array和Dictionary都是泛型集合,可以创建包含不同类型的数组。
为什么使用泛型
- 类型安全:对于插入容器中的对象的类型都是已知的
- 减少冗余代码:对多种数据类型进行相同操作,避免潜在的代码错误
- 灵活的依赖库:作为第三方库可以暴露一些接口,避免使用这些接口的开发者(同事?)被强制使用某种固定的类型。
食用方法
函数、方法、属性、下标以及构造器都可以当做泛型进行处理,它们自身可以成为泛型或者存在与某个泛型类型的上下文中。
类型参数是定义在<
与>
紧跟着方法名后,它可以作用于:
- 作为属性的类型
- 作为枚举体中的关联类型
- 作为某个方法的返回值或参数
- 作为构造器的参数类型
- 作为另一个泛型的类型参数,例如Array
// 重复添加到数组中
func duplicate<T>(item: T, numberOfTimes n: Int) -> [T] {
var buffer: [T] = []
for _ in 0 ..< n {
buffer.append(item)
}
return buffer
}
let obj = duplicate(item: "2333", numberOfTimes: 2)
// 这里的obj是类型[String]
4. Access Control 访问控制
访问级别
Swift中有五种访问级别: open
、public
、internal
、fileprivate
、private
,另外private(set)
修饰外部只读变量,相当于OC中的readonly
- open : module以外可以访问、重写或者继承。
- public: module以外可以访问、不能重写或者继承。
- internal(默认):当前module可用,无需任何import
- fileprivate: 当前文件可访问。 多用在声明extension中需要的成员变量或者方法
- private:当前声明区域可见
在实际项目中,我们的常用访问控制是这样的
class CameraViewController: UIViewController {
override func viewDidLoad() {
startRecording() // ✅使用fileprivate就可以在作用域外部访问到
endRecording() // ⛔这里编译报错,因为使用private只能在作用域内部访问到,这里无法访问到 endRecording()
}
}
extension ViewController {
fileprivate func startRecording() -> Void {
// ...
endRecording() // ✅这样可以正常访问
}
private func endRecording() -> Void { }
}
class WeReachability {
// 一些共用的属性需要用Public修饰保证多个文件都可以访问到
public enum WeReachabilityError: Error { }
}
Swift的设计目标之一就是一门安全的语言,所以才会有这么多访问权限,对于Swift3新出的
open
是为了弥补public语义上的不足。继承是一件危险的事情。尤其对于一个framework或者module的设计者而言。在自身的module内,类或者属性对于作者而言是清晰的,能否被继承或者override都是可控的。但是对于使用它的人,作者有时会希望传达出这个类或者属性不应该被继承或者修改。这个对应的就是 final。
final
的问题在于在标记之后,在任何地方都不能override。而对于lib的设计者而言,希望得到的是在module内可以被override,在被import到其他地方后其他用户使用的时候不能被override。
这就是 open产生的初衷。通过open和public标记区别一个元素在其他module中是只能被访问还是可以被override。
访问级别基本原则
- 不可以在某个实体中定义访问更低级别的实体
- 如果
private
级别的类型中定义了嵌套类型,那么该嵌套类型就自动拥有private访问级别 - 子类的访问级别不能高于父类的访问级别。不过可以通过重写为继承来的类成员提供更高的访问级别。
public class A {
private func someMethod() {
}
}
internal class B: A {
override internal func someMethod() {
}
}
5. Advanced Operators 高级运算符
这里可以查看官方文档完整的高级运算符