高级运算符
溢出运算符(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
原文链接:http://www.cnblogs.com/funkyRay/p/swift-jin-jie-shi-er-gao-ji-yun-suan-fu-kuo-zhan-f.html