Swift基础语法2(高级运算符、扩展Extension、访问控制)

524

高级运算符

溢出运算符

  • 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实现

  1. 没有管理按类型的枚举
  2. 只拥有遵守Equatable协议管理按类型的枚举
  3. 只拥有遵守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个实例的大小,一般做法是:
  1. 遵守Comparable协议
  2. 重载相应的运算符

自定义运算符(Custom Operator)

  • 可以自定义新的运算符:在全局作用域使用operator进行声明
  1. prefix operator前缀运算符
  2. postfix operator后缀运算符
  3. 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)
  • 扩展可以为枚举、结构体、类、协议添加新功能可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等。
  • 扩展不能办到的事情
  1. 不能覆盖原有的功能
  2. 不能添加存储属性,不能向已有的属性添加属性观察器
  3. 不能添加父类
  4. 不能添加指定初始化器,不能添加反初始化器
  5. ……

计算属性

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个不同的访问级别(以下是从高到低排列,实体指被访问级别修饰的内容)
  1. open:允许在定义实体的模块、其他模块中访问,允许其他模块进行继承、重写(open只能用在类、类成员上)
  2. public:允许在定义实体的模块、其他模块中访问,不允许其他模块进行继承、重写
  3. internal:只允许在定义实体的模块汇总访问,不允许在其他模块汇总访问
  4. fileprivate:只允许在定义实体的源文件中访问
  5. private:只允许在定义实体的封闭声明中访问
  • 绝大部分实体默认都是internal级别。

访问级别的使用准则

  • 一个实体不可以被更低访问级别的实体定义,比如:
  1. 变量\常量类型>=变量\常量
  2. 参数类型、返回值类型>=函数
  3. 父类>=子类
  4. 父协议>=子协议
  5. 原类型>=typealias
  6. 原始值类型、关联值类型>=枚举类型
  7. 定义类型A时用到的其他类型>=类型A
  8. ……

元组类型

  • 元组类型的访问级别是所有成员类型最低的那个

tuple
需要声明元组的访问级别,否则会编译报错

泛型类型

  • 泛型类型的访问级别是类型的访问级别以及所有泛型类型参数的访问级别中最低的那个

访问级别最低的是fileprivate class Dog,所以定义需要声明的级别要用fileprivate或者private。

成员、嵌套类型

  • 类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别一般情况下,类型为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