高級運算子
溢位運算子(Overflow Operator)
Swift的算數運算子出現溢位時會拋出運行時錯誤
Swift有溢位運算子&+、&-、&*,用來支持溢位運算
var min = UInt8.min
print(min &- 1) // 255, Int8.max
var max = UInt8.max
print(max &+ 1) // 0, Int8.min
print(max &* 2) // 254, 等價于 max &+ max
計算方式
類似于一個回圈,最大值255再+1,就會回到0;最小值0再-1,就會回到255
而max &* 2就等于max &+ max,也就是255 + 1 + 254,255 + 1會變為0,那么最后的值就是254

運算子多載(Operator Overload)
類、結構體、列舉可以為現有的運算子提供自定義的實作,這個操作叫做運算子多載
struct Point {
var x: Int, y: Int
}
func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
print(p) // Point(x: 21, y: 42) Point(x: 11, y: 22)
一般將運算子多載寫在相關的結構體、類、列舉的內部
struct Point {
var x: Int, y: Int
// 默認就是中綴運算子
static func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
static func - (p1: Point, p2: Point) -> Point {
Point(x: p1.x - p2.x, y: p1.y - p2.y)
}
// 前綴運算子
static prefix func - (p: Point) -> Point {
Point(x: -p.x, y: -p.y)
}
static func += (p1: inout Point, p2: Point) {
p1 = p1 + p2
}
static prefix func ++ (p: inout Point) -> Point {
p += Point(x: 1, y: 1)
return p
}
// 后綴運算子
static postfix func ++ (p: inout Point) -> Point {
let tmp = p
p += Point(x: 1, y: 1)
return tmp
}
static func == (p1: Point, p2: Point) -> Bool {
(p1.x == p2.x) && (p1.y == p2.y)
}
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 11, y: 22)
print(p1 + p2) // Point(x: 21, y: 42)
print(p2 - p1) // Point(x: 1, y: 2)
print(-p1) // Point(x: -10, y: -20)
p1 += p2
print(p1, p2) // Point(x: 21, y: 42) Point(x: 11, y: 22)
p1 = ++p2
print(p1, p2) // Point(x: 12, y: 23) Point(x: 12, y: 23)
p1 = p2++
print(p1, p2) // Point(x: 12, y: 23) Point(x: 13, y: 24)
print(p1 == p2) // false
Equatable
要想得知兩個實體是否等價,一般做法是遵守Equatable協議,多載==運算子
于此同時,等價于!=運算子
class Person: Equatable {
var age: Int
init(age: Int) {
self.age = age
}
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == rhs.age
}
}
var p1 = Person(age: 10)
var p2 = Person(age: 20)
print(p1 == p2) // false
print(p1 != p2) // true
如果沒有遵守Equatable協議,使用!=就會報錯

如果沒有遵守Equatable協議,只多載==運算子,也可以使用p1 == p2的判斷,但是遵守Equatable協議是為了能夠在有限制的泛型函式中作為引數使用
func equal<T: Equatable>(_ t1: T, _ t2: T) -> Bool {
t1 == t2
}
print(equal(p1, p2)) // false
Swift為以下型別提供默認的Equatable實作
沒有關聯型別的列舉
enum Answer {
case right, wrong
}
var s1 = Answer.right
var s2 = Answer.wrong
print(s1 == s2)
只擁有遵守Equatable協議關聯型別的列舉
系統很多自帶型別都已經遵守了Equatable協議,類似Int、Double等
enum Answer: Equatable {
case right, wrong(Int)
}
var s1 = Answer.wrong(20)
var s2 = Answer.wrong(10)
print(s1 == s2)
關聯型別沒有遵守Equatable協議的就會報錯

只擁有遵守Equatable協議存盤屬性的結構體
struct Point: Equatable {
var x: Int, y: Int
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 11, y: 22)
print(p1 == p2) // false
print(p1 != p2) // true
參考型別比較存盤的地址值是否相等(是否參考著同一個物件),使用恒等運算子===、!==
class Person {
}
var p1 = Person()
var p2 = Person()
print(p1 === p2) // false
print(p1 !== p2) // true
Comparable
要想比較兩個實體的大小,一般做法是遵守Comparable協議,多載相應的運算子
struct Student: Comparable {
var age: Int
var score: Int
init(score: Int, age: Int) {
self.score = score
self.age = age
}
static func < (lhs: Student, rhs: Student) -> Bool {
(lhs.score < rhs.score) || (lhs.score == rhs.score && lhs.age > rhs.age)
}
static func > (lhs: Student, rhs: Student) -> Bool {
(lhs.score > rhs.score) || (lhs.score == rhs.score && lhs.age < rhs.age)
}
static func <= (lhs: Student, rhs: Student) -> Bool {
!(lhs > rhs)
}
static func >= (lhs: Student, rhs: Student) -> Bool {
!(lhs < rhs)
}
}
var stu1 = Student(score: 100, age: 20)
var stu2 = Student(score: 98, age: 18)
var stu3 = Student(score: 100, age: 20)
print(stu1 > stu2) // true
print(stu1 >= stu2) // true
print(stu1 >= stu3) // true
print(stu1 <= stu3) // true
print(stu2 < stu1) // true
print(stu2 <= stu1) // true
自定義運算子(Custom Operator)
可以自定義新的運算子:在全域作用域使用operator進行宣告
prefix operator 前綴運算子
postfix operator 后綴運算子
infix operator 中綴運算子:優先級組
precedencegroup 優先級組 {
associativity: 結合性(left\right\none)
higherThan: 比誰的優先級更高
lowerThan: 比誰的優先級低
assignment: true代表在可選鏈操作中擁有跟賦值運算子一樣的優先級
}
自定義運算子的使用示例如下
prefix operator +++
prefix func +++ (_ i: inout Int) {
i += 2
}
var age = 10
+++age
print(age) // 12
infix operator +-: PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
associativity: none
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
assignment: true
}
struct Point {
var x = 0, y = 0
static func +- (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y - p2.y)
}
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 5, y: 10)
print(p1 +- p2) // Point(x: 15, y: 10)
優先級組中的associativity的設定影響
associativity對應的三個選項
left: 從左往右執行,可以多個進行結合
right: 從右往左執行,可以多個進行結合
none: 不支持多個結合
如果再增加一個計算就會報錯,因為我們設定的associativity為none

我們把associativity改為left或者right,再運行就可以了
infix operator +-: PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
associativity: left
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
assignment: true
}
var p3 = Point(x: 5, y: 10)
print(p1 +- p2 +- p3) // Point(x: 20, y: 0)
優先級組中的assignment的設定影響
我們先看下面的示例代碼
class Person {
var age = 0
var point: Point = Point()
}
var p: Person? = nil
print(p?.point +- Point(x: 10, y: 20))
設定assignment為true的意思就是如果在運算中,前面的可選項為nil,那么運算子后面的代碼就不會執行了
Apple檔案參考鏈接:
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations
另一個:
https://docs.swift.org/swift-book/ReferenceManual/Declarations.html
擴展(Extension)
基本概念
Swift中的擴展,類似于OC中的Category
擴展可以為列舉、類、結構體、協議添加新功能;可以添加方法、便捷初始化器、計算屬性、下標、嵌套型別、協議等
擴展不能做到以下這幾項
- 不能覆寫原有的功能
- 不能添加存盤屬性,不能向已有的屬性添加屬性觀察器
- 不能添加父類
- 不能添加指定初始化器,不能添加反初始化器
- ....
計算屬性、方法、下標、嵌套型別
extension Double {
var km: Double { self * 1_000.0 }
var m: Double { self }
var dm: Double { self / 10.0 }
var cm: Double { self / 100.0 }
var mm: Double { self / 1_000.0 }
}
extension Array {
subscript(nullable idx: Int) -> Element? {
if (startIndex..<endIndex).contains(idx) {
return self[idx]
}
return nil
}
}
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self { task() }
}
mutating func square() -> Int {
self = self * self
return self
}
enum Kind { case negative, zero, positive }
var kind: Kind {
switch self {
case 0: return .zero
case let x where x > 0: return .positive
default: return .negative
}
}
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex { decimalBase += 10 }
return (self / decimalBase) % 10
}
}
初始化器
class Person {
var age: Int
var name: String
init (age: Int, name: String) {
self.age = age
self.name = name
}
}
extension Person: Equatable {
static func == (left: Person, right: Person) -> Bool {
left.age == right.age && left.name == right.name
}
convenience init() {
self.init(age: 0, name: "")
}
}
如果希望自定義初始化器的同時,編譯器也能夠生成默認初始化器,可以在擴展中撰寫自定義初始化器
struct Point {
var x: Int = 0
var y: Int = 0
}
extension Point {
init(_ point: Point) {
self.init(x: point.x, y: point.y)
}
}
var p1 = Point()
var p2 = Point(x: 10)
var p3 = Point(y: 10)
var p4 = Point(x: 10, y: 20)
var p5 = Point(p4)
required的初始化器也不能寫在擴展中

協議
如果一個型別已經實作了協議的所有要求,但是還沒有宣告它遵守了這個協議,可以通過擴展來讓他遵守這個協議
protocol TestProtocol {
func test1()
}
class TestClass {
func test1() {
print("TestClass test1")
}
}
extension TestClass: TestProtocol { }
extension BinaryInteger {
func isOdd() -> Bool { self % 2 != 0 }
}
print(10.isOdd())
擴展可以給協議提供默認實作,也間接實作可選協議的結果
擴展可以給協議擴充協議中從未宣告過的方法
protocol TestProtocol {
func test1()
}
extension TestProtocol {
func test1() {
print("TestProtocol test1")
}
func test2() {
print("TestProtocol test2")
}
}
class TestClass: TestProtocol { }
var cls = TestClass()
cls.test1() // TestProtocol test1
cls.test2() // TestProtocol test2
var cls2: TestProtocol = TestClass()
cls2.test1() // TestProtocol test1
cls2.test2() // TestProtocol test2
class TestClass: TestProtocol {
func test1() {
print("TestClass test1")
}
func test2() {
print("TestClass test2")
}
}
var cls = TestClass()
cls.test1() // TestClass test1
cls.test2() // TestClass test2
var cls2: TestProtocol = TestClass()
cls2.test1() // TestClass test1
cls2.test2() // TestProtocol test2,編譯器會認為協議里沒有宣告的可能就不會去實作,所以優先找協議里的實作
泛型
class Stack<E> {
var elements = [E]()
func push(_ element: E) {
elements.append(element)
}
func pop() -> E {
elements.removeLast()
}
func size() -> Int {
elements.count
}
}
擴展中依然可以使用原型別中的泛型型別
extension Stack {
func top() -> E {
elements.last!
}
}
符合條件才擴展
extension Stack: Equatable where E : Equatable {
static func == (left: Stack, right: Stack) -> Bool {
left.elements == right.elements
}
}
訪問控制(Access Control)
基本概念
在訪問權限控制這塊,Swift提供了5個不同的訪問級別(以下是從高到低排列,物體指被訪問級別修飾的內容)
- open: 允許在定義物體的模塊、其他模塊中訪問,允許其他模塊進行繼承、重寫(open只能用在類、類成員上)
- public: 允許在定義物體的模塊、其他模塊中訪問,不允許其他模塊進行繼承、重寫
- internal: 只允許在定義物體的模塊中訪問,不允許在其他模塊中訪問
- fileprivate: 只允許在定義物體的源檔案中訪問
- private: 只允許在定義物體的封閉宣告中訪問
絕大部分物體默認都是internal級別
訪問級別的使用準則
一個物體不可以被更低訪問級別的物體定義
變數\常量型別 ≥ 變數\常量
internal class Person {} // 變數型別
fileprivate var person: Person // 變數

引數型別、回傳值型別 ≥ 函式
// 引數型別:Int、Double
// 函式:func test
internal func test(_ num: Int) -> Double {
return Double(num)
}
父類 ≥ 子類
class Person {}
class Student: Person {}
public class Person {}
class Student: Person {}

父協議 ≥ 子協議
public protocol Sportable {}
internal protocol Runnalbe: Sportable {}

原型別 ≥ typealias
class Person {} // 原型別
private typealias MyPerson = Person
原始值型別\關聯值型別 ≥ 列舉型別
typealias MyInt = Int
typealias MyString = String
enum Score {
case point(MyInt)
case grade(MyString)
}

定義型別A時用到的其他型別 ≥ 型別A
typealias MyString = String
struct Dog {}
class Person {
var age: MyString = ""
var dog: Dog?
}

元組型別
元組型別的訪問級別是所有成員型別最低的那個
internal struct Dog { }
fileprivate class Person { }
// (Dog, Person)中更低的訪問級別是fileprivate,所以元組的訪問級別就是fileprivate
fileprivate var datal: (Dog, Person)
private var data2: (Dog, Person)
泛型型別
泛型型別的訪問級別是型別的訪問級別以及所有泛型型別引數的訪問級別中最低的那個
internal class Car {}
fileprivate class Dog {}
public class Person<T1, T2> {}
// Person<Car, Dog>中比較的是Person、Car、Dog三個的訪問級別最低的那個,也就是fileprivate,fileprivate就是泛型型別的訪問級別
fileprivate var p = Person<Car, Dog>()
成員、嵌套型別
型別的訪問級別會影響成員(屬性、方法、初始化器、下標),嵌套型別的默認訪問級別
一般情況下,型別為private或fileprivate,那么成員\嵌套型別默認也是private或fileprivate
fileprivate class FilePrivateClass { // fileprivate
func f1() {} // fileprivate
private func f2() {} // private
}
private class PrivateClass { // private
func f() {} // private
}
一般情況下,型別為internal或public,那么成員\嵌套型別默認是internal
public class PublicClass { // public
public var p1 = 0 // public
var p2 = 0 // internal
fileprivate func f1() {} // fileprivate
private func f2() {} // private
}
class InternalClass { // internal
var p = 0 // internal
fileprivate func f1() {} // fileprivate
private func f2() {} // private
}
看下面幾個示例,編譯能否通過?
示例1
private class Person {}
fileprivate class Student: Person {}
class Test {
private class Person {}
fileprivate class Student: Person {}
}
結果是第一段代碼編譯通過,第二段代碼編譯報錯

第一段代碼編譯通過,是因為兩個全域變數不管是private還是fileprivate,作用域都是當前檔案,所以訪問級別就相同了
第二段代碼的兩個屬性的作用域局限到類里面了,那訪問級別就有差異了
示例2
private struct Dog {
var age: Int = 0
func run() {}
}
fileprivate struct Person {
var dog: Dog = Dog()
mutating func walk() {
dog.run()
dog.age = 1
}
}
private struct Dog {
private var age: Int = 0
private func run() {}
}
fileprivate struct Person {
var dog: Dog = Dog()
mutating func walk() {
dog.run()
dog.age = 1
}
}
結果是第一段代碼編譯通過,第二段代碼編譯報錯

第一段代碼編譯通過,是因為兩個結構體的訪問級別都是該檔案內,所以訪問級別相同
第二段代碼報錯是因為Dog里的屬性和方法的訪問級別是更低的了,雖然兩個結構體的訪問級別相同,但從Person里呼叫Dog中的屬性和方法是訪問不到的
結論:直接在全域作用域下定義的private等于fileprivate
成員的重寫
子類重寫成員的訪問級別必須 ≥ 子類的訪問級別,或者 ≥ 父類被重寫成員的訪問級別
class Person {
internal func run() {}
}
fileprivate class Student: Person {
fileprivate override func run() {}
}
父類的成員不能被成員作用域外定義的子類重寫

放到同一個作用域下
public class Person {
private var age: Int = 0
public class Student: Person {
override var age: Int {
set {}
get { 10 }
}
}
}
getter、setter
getter、setter默認自動接收它們所屬環境的訪問級別
可以給setter單獨設定一個比getter更低的訪問級別,用以限制寫的權限
fileprivate(set) public var num = 10
num = 10
print(num)

初始化器
如果一個public類想在另一個模塊呼叫編譯生成的默認無參初始化器,必須顯式提供public的無參初始化器,因為public類的默認初始化器是internal級別
public class Person {
// 默認生成的,因為是internal,所以外部無法呼叫到該初始化器
// internal init() {
//
// }
}
變成這樣
public class Person {
// 自己手動添加指定初始化器,并用public修飾,外部才能訪問的到
public init() {
}
}
required初始化器 ≥ 它的默認訪問級別
fileprivate class Person {
internal required init() {}
}
當類是public的時候,它的默認初始化器就是internal級別的,所以不會報錯
public class Person {
internal required init() {}
}

如果結構體有private\fileprivate的存盤實體屬性,那么它的成員初始化器也是private\fileprivate,否則默認就是internal

結構體里有一個屬性設定為private,帶有其他屬性的初始化器也沒有了

列舉型別的case
不能給enum的每個case單獨設定訪問級別

每個case自動接收enum的訪問級別
fileprivate enum Season {
case spring // fileprivate
case summer // fileprivate
case autumn // fileprivate
case winter // fileprivate
}
public enum定義的case也是public
public enum Season {
case spring // public
case summer // public
case autumn // public
case winter // public
}
協議
協議中定義的要求自動接收協議的訪問級別,不能單獨設定訪問級別

public協議定義的要求也是public
public protocol Runnable {
func run()
}
協議實作的訪問級別必須 ≥ 型別的訪問級別,或者 ≥ 協議的訪問級別


擴展
如果有顯式設定擴展的訪問級別,擴展添加的成員自動接收擴展的訪問級別
class Person {
}
private extension Person {
func run() {} // private
}
如果沒有顯式設定擴展的訪問級別,擴展添加的成員的默認訪問級別,跟直接在型別中定義的成員一樣
private class Person {
}
extension Person {
func run() {} // private
}
可以單獨給擴展添加的成員設定訪問級別
class Person {
}
extension Person {
private func run() {}
}
不能給用于遵守協議的擴展顯式設定擴展的訪問級別

在同一檔案中的擴展,可以寫成類似多個部分的型別宣告
在原本的宣告中宣告一個私有成員,可以在同一個檔案的擴展中訪問它
在擴展中宣告一個私有成員,可以在同一檔案的其他擴展中、原本宣告中訪問它
public class Person {
private func run0() {}
private func eat0() {
run1()
}
}
extension Person {
private func run1() {}
private func eat1() {
run0()
}
}
extension Person {
private func eat2() {
run1()
}
}
將方法賦值給var\let
方法也可以像函式那樣,賦值給一個let或者var
struct Person {
var age: Int
func run(_ v : Int) { print("func run", age, v)}
static func run(_ v: Int) { print("static func run", v)}
}
let fn1 = Person.run
fn1(10) // static func run 10
let fn2: (Int) -> () = Person.run
fn2(20) // static func run 20
let fn3: (Person) -> ((Int) -> ()) = Person.run
fn3(Person(age: 18))(30) // func run 18 30
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/275378.html
標籤:iOS
下一篇:安卓平滑的翻動效果
