高级运算符
溢出运算符
- swift的算术运算符出现溢出会抛出运行时错误
- Swift的溢出运算符(&+、&-、&*),用来支持溢出运算符
var min = UInt8.min
print(min &- 1) // 255,等于UInt8.max
var max = UInt8.max
print(max &+ 1) // 0,等于UInt8.min
print(max &* 2) // 254,等于UInt8.max + UInt8.max
UInt取值范围是0~255,溢出只会会有闭环从反方向开始。
运算符重载(Operator Overload)
- 类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载
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 temp = p
p += Point(x:1,y:1)
return temp
}
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: 30, y: 40)
var p3 = p1 + p2
print(p3) // Point(x: 40, y: 60)
var p4 = -p1
print(p4) // Point(x: -10, y: -20)
var p5 = ++p1
print(p5) //Point(x: 11, y: 21)
Equatable
- 想要得知两个实例是否,一般做法是遵守Equatable协议,重载 == 运算符,与此同时,等价于重载了!=运算符
struct Point : Equatable {
var x:Int, y:Int
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 11, y: 22)
var p3 = Point(x: 10, y: 20)
print(p1 == p2) // false
print(p1 != p2) // true
print(p1 == p3) // true
*Swift为以下类型提供默认的Equatable实现
- 没有管理按类型的枚举
- 只拥有遵守Equatable协议管理按类型的枚举
- 只拥有遵守Equatable协议存储属性的结构体 引用类型比较存储的地址值是否相等(是否引用着同一个对象)使用恒等运算符===、!==
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: 80, age: 20)
var stu2 = Student(score: 80, age: 18)
var stu3 = Student(score: 90, age: 30)
print(stu2 > stu1) // true
print(stu2 > stu3) // false
- 想要比较2个实例的大小,一般做法是:
- 遵守Comparable协议
- 重载相应的运算符
自定义运算符(Custom Operator)
- 可以自定义新的运算符:在全局作用域使用operator进行声明
- prefix operator前缀运算符
- postfix operator后缀运算符
- infix operator 重罪运算符:优先级组
precedencegroup 优先级数组 {
associativity:left// 结合性(left\right\non)
higherThan:AdditionPrecedence // 比那个运算符的优先级高
lowerThan:MultiplicationPrecedence // 比哪个优先级低
assignment:true // 代表在可选链操作中拥有跟赋值运算符一样的优先级
}
案例:
// 需要在外部声明运算符
prefix operator +++
infix operator +-
struct Point {
var x:Int, y:Int
static prefix func +++ (point: inout Point) -> Point {
point = Point(x: point.x + point.y, y: point.y + point.y)
return point
}
static func +-(left:Point,right:Point) -> Point {
return Point(x: left.x + right.x, y: left.y - right.y)
}
}
struct Person {
var point:Point
}
var p1 = Person(point: Point(x: 10, y: 20))
p1.point = p1.point +- Point(x: 1, y: 2)
print(p1.point) // Point(x: 11, y: 18)
var p2 = Person(point: Point(x: 20, y: 30))
_ = +++p2.point
print(p2.point) // Point(x: 50, y: 60)
扩展(Extension)
- Swift中的扩展,有点类似OC中的分类(Category)
- 扩展可以为枚举、结构体、类、协议添加新功能可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等。
- 扩展不能办到的事情
- 不能覆盖原有的功能
- 不能添加存储属性,不能向已有的属性添加属性观察器
- 不能添加父类
- 不能添加指定初始化器,不能添加反初始化器
- ……
计算属性
extension Double {
var km: Double { self / 1_0000.0}
var m:Double {self}
var dm:Double {self * 10.0}
var cm:Double {self * 100.0}
var mm:Double {self * 1_000.0}
}
var abc:Double = 200
print("abc-cm=\(abc.cm)") // abc-cm=20000.0
下标
extension Array {
subscript(nullable idx: Int) -> Element? {
if (startIndex ..< endIndex).contains(idx) {
return self[idx]
}
return nil
}
}
var myArr = [1,2,3,4,5]
print(myArr[nullable:1]) // Optional(2)
print(myArr[nullable: 10]) // 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
}
}
var myIn = 131
print (myIn[1]) // 3
协议、初始化器
class Person {
var age:Int
var name: String
init(age:Int, name:String) {
self.age = age
self.name = name
}
}
extension Person : Equatable {
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == rhs.age && lhs.name == rhs.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:20)
var p4 = Point(x:10,y:20)
- 如果希望自定义初始化器的同时,编译器也能够生成默认初始化器可以在扩展中编写自定义初始化器
required
初始化器也不能写在扩展中。- 如果一个协议已经实现了协议的所有要求,但是还没有声明它遵守了这个协议,可以通过扩展来让它遵守这个协议
- 扩展可以给协议提供默认实现,也间接实现「可选协议」的效果
- 扩展可以给协议扩充「协议中从未声明过的方法」
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 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 == (lhs: Stack<E>, rhs: Stack<E>) -> Bool {
lhs.elements == rhs.elements
}
}
访问控制(Access Control)
- 在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列,实体指被访问级别修饰的内容)
open
:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上)public
:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写internal
:只允许在定义实体的模块汇总访问,不允许在其他模块汇总访问fileprivate
:只允许在定义实体的源文件中访问private
:只允许在定义实体的封闭声明中访问
- 绝大部分实体默认都是
internal
级别。
访问级别的使用准则
- 一个实体不可以被更低访问级别的实体定义,比如:
- 变量\常量类型>=变量\常量
- 参数类型、返回值类型>=函数
- 父类>=子类
- 父协议>=子协议
- 原类型>=typealias
- 原始值类型、关联值类型>=枚举类型
- 定义类型A时用到的其他类型>=类型A
- ……
元组类型
- 元组类型的访问级别是所有成员类型最低的那个
泛型类型
- 泛型类型的访问级别是
类型的访问级别
以及所有泛型类型参数的访问级别
中最低的那个
成员、嵌套类型
- 类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别一般情况下,类型为private或fileprivate,那么成员\嵌套类型默认也是private或fileprivate,一般情况下,类型为internal或public,那么成员\嵌套类型默认是internal
public class PublicClass {
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
}
fileprivate class FilePrivateClass { // fileprivate
func f1() { } // fileprivate
private func f2() {} // private
}
private class PrivateClass { // private
func f() {} // private
}
成员的重写
- 子类重写成员的访问级别必须>=子类的访问级别,或者>=父类被重写成员的访问级别
- 父类的成员不能被成员作用域外定义的子类重写
在作用域外的子类 在作用域内的子类
常见作用域举例
- 直接在全局作用域下定义的private等价于fileprivate
getter、setter
- getter、setter默认自动接收它们所属环境的访问级别
- 可以给setter单独设置一个比getter更低的访问级别,用以限制写的权限
fileprivate(set) public var num = 10
class Person {
private(set) var age = 0
fileprivate(set) public var weight:Int {
set{}
get{}
}
internal(set) public subscript(index:Int) -> Int {
set{}
get {index}
}
}
初始化器
- 如果一个public类想在另一个模块调用编译生成的默认无参初始化器,必须显式提供public的无参初始化器,因为public类的默认初始化器是internal级别
- required初始化器>=它的默认访问级别
- 如果结构体有private\fileprivate的存储实例属性,那么它的成员初始化器也是private\fileprivate,否则默认就是internal
枚举类型的case
- 不能给enum的每个case单独设置访问级别
- 每个case自动接收enum的访问级别,public enum定义的case也是public
协议
- 协议中定义的要求自动接收协议的访问级别,不能单独设置访问级别,public协议定义的要求也是public
- 协议实现的访问级别必须>=类型的访问级别,或者>=协议的访问级别
一个类在其他类中继承协议不需要考虑协议的作用域
扩展
- 如果有显式设置扩展的访问级别,扩展添加的成员自动接收扩展的访问级别
- 如果没有显式设置扩展的访问级别,扩展添加的成员的默认访问级别,跟直接在类型中定义的成员一样
- 可以单独给扩展添加的成员设置访问级别
- 不能给用于遵守协议的拓展显式设置拓展的访问级别
- 在同一个文件中的扩展,可以写成类似多个部分的类型声明,
- 在原本的声明中声明一个私有成员,可以在同一文件的扩展中访问它
- 在扩展中声明一个私有成员,可以在同一文件的其他扩展中、原本声明中访问它
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