本文是个比较简单的学习笔记,更详细的内容见 Swift官方文档
Swift标准库用 < 和 == 运算符定义了 >、>=、<=,所以实现 Comparable 的 < 运算符就会自动得到这些运算符的实现,实际上 Comparable 继承自 Equatable,所以 == 运算也是必须实现的,以下是示例代码
struct Point: Comparable, CustomStringConvertible {
let x: Int
let y: Int
var description: String {
return "Point(\(x), \(y))"
}
static func ==(lhs: Point, rhs: Point) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
static func <(lhs: Point, rhs: Point) -> Bool {
return lhs.x < rhs.x && lhs.y < rhs.y
}
static func + (left: Point, right: Point) -> Point {
return Point(x: left.x + right.x, y: left.y + right.y)
}
}
let a = Point(x: 3, y: 4)
let b = Point(x: 3, y: 4)
let abEqual = (a == b)
a != b
let c = Point(x: 2, y: 6)
let d = Point(x: 3, y: 7)
c == d
c < d
c <= d
c > d
c >= d
c + d
class Person: Equatable {
let name: String
let age: Int
weak var brother: Person?
init(name: String, age: Int) {
self.name = name
self.age = age
}
static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name
}
}
let p1 = Person(name: "JJF", age: 33)
let p2 = Person(name: "JZF", age: 36)
var people = [p1, p2]
if let p1Index = people.firstIndex(where: { $0 == p2 }) {
print(p1Index)
}
//自定义运算符
infix operator +++
func +++(lhs: Person, rhs: Person) {
lhs.brother = rhs
rhs.brother = lhs
}
p1 +++ p2
p1.brother?.name
p2.brother?.name
import Foundation
let date = Date.now
let dateFormatter = DateFormatter()
//dateFormatter.locale = Locale(identifier: "zh_CN")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
// typealias TimeInterval = Double
// A number of seconds.
// TimeInterval 是 Double 类型,单位是秒,可精确到微秒
//时间戳,1970到现在的毫秒数
let timestamp = Int(date.timeIntervalSince1970 * 1000)
print("timestamp = \(timestamp)")
//取当前时间的年/月/日/时/分/秒/毫秒
var calendar = Calendar.current
let year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)
let second = calendar.component(.second, from: date)
let milliSecond = calendar.component(.nanosecond, from: date) / 1000_000
print("\(year)-\(String(format: "%02D", month))-\(String(format: "%02D", day)) \(String(format: "%02D", hour)):\(String(format: "%02D", minute)):\(String(format: "%02D", second)).\(String(format: "%03D", milliSecond))")
//使用 DateFormatter 格式化时间
print("\(dateFormatter.string(from: date))")
let str = "Hello World!"
let start = str.index(str.startIndex, offsetBy: 6)
let end = str.index(str.startIndex, offsetBy: 11)
let substr = String(str[start..
enum Token {
case number(Int)
case plus
}
class Lexer {
enum Error: Swift.Error {
case invalidCharacter(Character)
}
let input: String
var position: String.Index
init(input: String) {
self.input = input
self.position = self.input.startIndex
}
func peek() -> Character? {
guard position < input.endIndex else {
return nil
}
return input[position]
}
func advance() {
//断言只在调试模式下生效,发布模式下可使用 precondition(_:_:) 替代
assert(position < input.endIndex, "Cannot advance past endIndex!")
position = input.index(after: position)
}
func lex() throws -> [Token] {
var tokens = [Token]()
while let nextCharacter = peek() {
switch nextCharacter {
case "0"..."9":
tokens.append(.number(getNumber()))
case "+":
tokens.append(.plus)
advance()
case " ":
advance()
default:
throw Lexer.Error.invalidCharacter(nextCharacter)
}
}
return tokens
}
func getNumber() -> Int {
var value = 0
while let nextCharacter = peek() {
switch nextCharacter {
case "0"..."9":
let digitValue = Int(String(nextCharacter))!
value = 10 * value + digitValue
advance()
default:
return value
}
}
return value
}
}
class Parser {
enum Error: Swift.Error {
case unexpectedEndOfInput
case invalidToken(Token)
}
let tokens: [Token]
var position = 0
init(tokens: [Token]) {
self.tokens = tokens
}
func nextToken() -> Token? {
guard position < tokens.count else {
return nil
}
let token = tokens[position]
position += 1
return token
}
func getNumber() throws -> Int {
guard let token = nextToken() else {
throw Parser.Error.unexpectedEndOfInput
}
switch token {
case .number(let value):
return value
case .plus:
throw Parser.Error.invalidToken(token)
}
}
func parse() throws -> Int {
var value = try getNumber()
while let token = nextToken() {
switch token {
case .plus:
let nextNumber = try getNumber()
value += nextNumber
case .number:
throw Parser.Error.invalidToken(token)
}
}
return value
}
}
func evaluate(_ input: String) {
print("Evaluating: \(input)")
let lexer = Lexer(input: input)
// let tokens = try! lexer.lex()//强制执行可能抛出错误的方法,发生错误时触发陷阱
// guard let tokens = try? lexer.lex() else {
// print("Lexing failed, but I don't know why")
// return
// }
do {
let tokens = try lexer.lex()
print("Lexer ouput: \(tokens)")
let parser = Parser(tokens: tokens)
let value = try parser.parse()
print(value)
} catch Lexer.Error.invalidCharacter(let character) {
print("Input contained an invalid character: \(character)")
} catch Parser.Error.unexpectedEndOfInput {
print("Unexpected end of input during parsing")
} catch Parser.Error.invalidToken(let token) {
print("Invalid token during parsing: \(token)")
} catch {
print("An error occurred: \(error)")
}
}
evaluate("10 + 3 + 5 +")
typealias Velocity = Double
extension Velocity {
var kph: Velocity { return self * 1.60934 }
var mph: Velocity { return self }
}
protocol Vehicle {
var topSpeed: Velocity { get }
var numberOfDoors: Int { get }
var hasFlatbed: Bool { get }
}
struct Car {
let make: String
let model: String
let year: Int
let color: String
let nickname: String
var gasLevel: Double {
willSet {
precondition(newValue <= 1.0 && newValue >= 0.0, "New value must between 0 and 1.")
}
}
}
//利用拓展使 Car 实现协议
extension Car: Vehicle {
var topSpeed: Velocity { return 180 }
var numberOfDoors: Int { return 4 }
var hasFlatbed: Bool { return false }
}
//利用拓展给 Car 增加初始化方法,这样会保留默认的成员初始化方法
extension Car {
init(make: String, model: String, year: Int) {
self.init(make: make, model: model, year: year, color: "Black", nickname: "N/A", gasLevel: 1.0)
}
}
var c = Car(make: "Ford", model: "Fusion", year: 2013)
extension Car {
enum Kind {
case coupe, sedan
}
var kind: Kind {
if numberOfDoors == 2 {
return .coupe
} else {
return .sedan
}
}
}
c.kind
extension Car {
mutating func emptyGas(by amount: Double) {
precondition(amount <= 1 && amount > 0, "Amount to remove must be between 0 and 1.")
gasLevel -= amount
}
mutating func fillGas() {
gasLevel = 1.0
}
}
c.emptyGas(by: 0.3)
c.gasLevel
c.fillGas()
c.gasLevel
protocol Exercise: CustomStringConvertible {
var name: String { get }
var caloriesBurned: Double { get } //消耗的卡路里
var minutes: Double { get }//锻炼的时长
}
extension Exercise {
var description: String {
return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"
}
}
extension Exercise {
var title: String {
return "\(name) - \(minutes) minutes"
}
}
struct EllipticalWorkout: Exercise {
let name: String = "Elliptical Workout"
let title: String = "Workout using the Go Fast Elliptical Trainer 3000"
let caloriesBurned: Double
let minutes: Double
}
struct TreadmillWorkout: Exercise {
let name: String = "Treadmill Workout"
let caloriesBurned: Double
let minutes: Double
let laps: Double //跑步的圈数
}
extension TreadmillWorkout {
var description: String {
return "TreadmillWorkout(\(caloriesBurned) calories and \(laps) laps in \(minutes) minutes)"
}
}
let ellipticalWorkout = EllipticalWorkout(caloriesBurned: 335, minutes: 30)
let runningWorkout = TreadmillWorkout(caloriesBurned: 350, minutes: 25, laps: 10.5)
//使用范型定义函数:计算每分钟消耗的卡路里
func caloriesBurnedPerMinute(for exercise: E) -> Double {
return exercise.caloriesBurned / exercise.minutes
}
print(caloriesBurnedPerMinute(for: ellipticalWorkout))
print(caloriesBurnedPerMinute(for: runningWorkout))
//拓展协议
extension Exercise {
//所有实现该协议的类型都可以使用通过拓展为该协议添加的属性和方法
var caloriesBurnedPerMinute: Double {
return caloriesBurned / minutes
}
}
print(ellipticalWorkout.caloriesBurnedPerMinute)
print(runningWorkout.caloriesBurnedPerMinute)
print(ellipticalWorkout)
print(runningWorkout)
//带 where 子句的协议拓展
extension Sequence where Iterator.Element == Exercise {
func totalCaloriesBurned() -> Double {
var total: Double = 0
for exercise in self {
total += exercise.caloriesBurned
}
return total
}
}
let mondayWorkout: [Exercise] = [ellipticalWorkout, runningWorkout]
print(mondayWorkout.totalCaloriesBurned())
for exercise in mondayWorkout {
print(exercise.title)
}
print(ellipticalWorkout.title)
struct StackIterator: IteratorProtocol {
// typealias Element = T
var stack: Stack
mutating func next() -> T? {
return stack.pop()
}
}
// Sequence 是实现 for-in 循环在内部使用的协议
struct Stack: Sequence {
var items = [E]()
// 使用 where 子句约束 Sequence 的元素类型
mutating func pushAll(_ sequence: S) where S.Iterator.Element == E {
for item in sequence {
self.push(item)
}
}
mutating func push(_ newItem: E) {
items.append(newItem)
}
mutating func pop() -> E? {
guard !items.isEmpty else {
return nil
}
return items.removeLast()
}
func map(_ f: (E) -> U) -> Stack {
var mappedItems = [U]()
for item in items {
mappedItems.append(f(item))
}
return Stack(items: mappedItems)
}
func makeIterator() -> StackIterator {
return StackIterator(stack: self)
}
}
var intStack = Stack()
intStack.push(1)
intStack.push(2)
var doubledStack = intStack.map { 2 * $0 }
print(intStack.pop())
print(intStack.pop())
print(intStack.pop())
print(doubledStack.pop())
print(doubledStack.pop())
func myMap(_ items: [T], _ f: (T) -> U) -> [U] {
var result = [U]()
for item in items {
result.append(f(item))
}
return result
}
let strings = ["one", "two", "three"]
//let stringLengths = myMap(strings, {(str: String) -> Int in
// return str.count
//})
let stringLengths = myMap(strings) { $0.count }
print(stringLengths)
//类型约束
func checkIfEqual(_ first: T, _ second: T) -> Bool {
return first == second
}
print(checkIfEqual(1, 1))
print(checkIfEqual("a string", "a string"))
print(checkIfEqual("a string", "a different string"))
//每个占位类型都可以有一个类型约束
func checkIfDescriptionsMatch(_ first: T, _ second: U) -> Bool {
return first.description == second.description
}
print(checkIfDescriptionsMatch(Int(1), UInt(1)))
print(checkIfDescriptionsMatch(1, 1.0))
print(checkIfDescriptionsMatch(Float(1.0), Double(1.0)))
var myStack = Stack()
myStack.push(10)
myStack.push(20)
myStack.push(30)
var myStackIterator = StackIterator(stack: myStack)
while let value = myStackIterator.next() {
print("got \(value)")
}
for value in myStack {
print("for-in loop: got \(value)")
}
myStack.pushAll([1, 2, 3])
for value in myStack {
print("after pushing: got \(value)")
}
var myOtherStack = Stack()
myOtherStack.pushAll([1, 2, 3])
myStack.pushAll(myOtherStack)
for value in myStack {
print("after pushing item onto stack, got \(value)")
}
protocol TabularDataSource {
var numberOfRows: Int { get }
var numberOfColumns: Int { get }
func label(forColumn column: Int) -> String
func itemFor(row: Int, column: Int) -> String
}
protocol PrintableTabularDataSource: TabularDataSource, CustomStringConvertible {
//
}
//组合协议,让 printTable 的参数符合 CustomStringConvertible 协议
func printTable(_ dataSource: TabularDataSource & CustomStringConvertible) {
print(dataSource)
var firstRow = "|"
var columnWidths = [Int]()
for i in 0.. String {
switch column {
case 0: return "Employee Name"
case 1: return "Age"
case 2: return "Years of Experience"
default: fatalError("Invalid column!")
}
}
func itemFor(row: Int, column: Int) -> String {
let person = people[row]
switch column {
case 0: return person.name
case 1: return String(person.age)
case 2: return String(person.yearsOfExperience)
default: fatalError("Invalid column!")
}
}
}
var department = Department(name: "Engineering")
department.add(Person(name: "Joe", age: 30, yearsOfExperience: 6))
department.add(Person(name: "Karen", age: 40, yearsOfExperience: 18))
department.add(Person(name: "Fred", age: 50, yearsOfExperience: 20))
printTable(department)
import Cocoa
extension String {
/// 截取字符串
/// - Parameter start: 起始索引(包含)
/// - Parameter end: 结束索引(不包含)
func substring(start: Int, end: Int) -> String {
let firstIndex = index(startIndex, offsetBy: start)
let lastIndex = index(startIndex, offsetBy: end)
return String(self[firstIndex.. Data {
let hex = self.replacingOccurrences(of: " ", with: "")
var arr = Array()
for i in 0.. String {
return subdata2ascii(start: 0, end: self.count)
}
/// 转换成16进制字符串
/// - Parameter withSpace: 可选参数,默认true,输出的字符串是否每两个字节之间放一个空格
func toHex(withSpace: Bool = true) -> String {
return subdata2hex(start: 0, end: self.count, withSpace: withSpace)
}
/// 将指定范围的数据转换成ASCII字符串
/// - Parameter start: 起始索引(包含)
/// - Parameter end: 结束索引(不包含)
func subdata2ascii(start: Int, end: Int) -> String {
if let str = String(data: self[start.. String {
let arr = Array(self[start.. String in
return String(format: "%02X", byte)
}).joined(separator: withSpace ? " " : "")
return hex
}
}
func test() {
let hexString = "5A 58 48 0C 83 0E 47 07 22 2B 42 41 30 34 56 30 35"
let data = hexString.toData()
print("head = \(data.subdata2ascii(start: 0, end: 3))")
print("mac = \(data.subdata2hex(start: 3, end: 9))")
print("DeviceType = \(data.subdata2ascii(start: 9, end: 14))")
print("version = \(data.subdata2ascii(start: 14, end: data.count))")
print(data.toHex(withSpace: true))
}
test()
let apples = 3
let oranges = 5
//只要与结束引号(""")的缩进匹配,就会删除每一行开始处的缩进
let quotation = """
Even though there's whitespace to the left,
the actual lines aren't indented.
Except for this line.
Double quotes (") can appear without being escaped.
I still have \(apples + oranges) pieces of fruit.
"""
print(quotation)
/** struct 类似 Kotlin 的 data class,等价写法是:
data class Person(var firstName: String = "Matt", var lastName: String = "Mathias")
*/
struct Person {
var firstName = "Matt"
var lastName = "Mathias"
}
var p = Person(firstName: "Jia", lastName: "Jiefei")
print(p)
//如果属性没有提供默认值,结构体会根据属性生成默认的成员初始化方法,并且直接打印结构体可以看到所有存储属性的值
struct Device {
var name: String
var address: String
// text 是计算属性,打印时不会输出
var text: String {
return "name=\(name), address=\(address)"
}
}
print(Device(name: "Device", address: "11:22:33:44:55:66"))
//而类就没有默认成员初始化方法了,需要自己编写初始化方法,并且直接打印类实例,不会输出各存储属性的值
class BleDevice {
var name: String
var address: String
required init(name: String, address: String) {
self.name = name
self.address = address
}
}
print(BleDevice(name: "BleDevice", address: "AA:BB:CC:DD:EE:FF"))
//类是引用类型
class GreekGod {
var name: String
init(name: String) {
self.name = name
}
}
let hecate = GreekGod(name: "Hecate")
let athena = GreekGod(name: "Athena")
let zeus = GreekGod(name: "Zeus")
let anotherHecate = hecate
anotherHecate.name = "AnotherHecate"
hecate.name //类是引用类型,改变 anotherHecate.name,hecate.name也会变
//结构体是值类型
struct Pantheon {
var chiefGod: GreekGod
}
let pantheon = Pantheon(chiefGod: hecate)
pantheon.chiefGod.name // AnotherHecate
let greekPantheon = pantheon
hecate.name = "Trivia"
greekPantheon.chiefGod.name // Trivia
let gods = [athena, hecate, zeus]
let godsCopy = gods
gods.last?.name = "Jupiter"
godsCopy.last?.name // Jupiter
//探究写时复制(copy on write, COW)
fileprivate class IntArrayBuffer {
var storage: [Int]
init() {
storage = []
}
init(buffer: IntArrayBuffer) {
storage = buffer.storage
}
}
struct IntArray {
private var buffer: IntArrayBuffer
init() {
buffer = IntArrayBuffer()
}
func describe() {
let str = buffer.storage.map { i in
String(i)
}.joined(separator: ", ")
print("[\(str)]")
// print(buffer.storage)
}
private mutating func copyIfNeeded() {
//判断引用类型是否只有一个引用
if !isKnownUniquelyReferenced(&buffer) {
print("Making a copy of \(buffer.storage)")
buffer = IntArrayBuffer(buffer: buffer)
}
}
mutating func insert(_ value: Int, at index: Int) {
copyIfNeeded()
buffer.storage.insert(value, at: index)
}
mutating func append(_ value: Int) {
copyIfNeeded()
buffer.storage.append(value)
}
mutating func remove(at index: Int) {
copyIfNeeded()
buffer.storage.remove(at: index)
}
}
var integers = IntArray()
integers.append(1)
integers.append(2)
integers.append(4)
integers.describe()
var ints = integers
ints.insert(3, at: 2)
integers.describe()
ints.describe()
虽然是讲继承,但代码中的注释也会涉及其他知识点!例子涉及3个.swift文件
Town.swift
struct Town {
let region: String
var population: Int {
didSet {
print("The population has changed to \(population) from \(oldValue)")
}
}
var numberOfStoplights: Int
//自定义初始化方法,问号表示可失败的初始化方法
init?(region: String, population: Int, stoplights: Int) {
guard population > 0 else {
return nil
}
self.region = region
self.population = population
numberOfStoplights = stoplights
}
//委托初始化
init?(population: Int, stoplights: Int) {
self.init(region: "N/A", population: population, stoplights: stoplights)
}
enum Size {
case small
case medium
case large
}
//计算属性
var townSize: Size {
get {
print("town size")//每次调用都会打印
switch self.population {
case 0...10_000:
return Size.small
case 10_001...100_000:
return Size.medium
default:
return Size.large
}
}
}
//惰性存储属性,注意这里的等号以及结尾的圆括号
lazy var name: String = {
print("town name")//这里只会打印一次,即第一次访问该属性时
return "XiShe"
}()
func printDescription() {
print("Population: \(population), number of stoplights: \(numberOfStoplights), region: \(region)")
}
/* 如果结构体(确切的说,应该是值类型,结构体和枚举都是值类型)的一个实例方法需要修改结构体的属性,就必须使用 mutating 标记该方法 */
mutating func changePopulation(by amount: Int) {
population += amount
}
/* 对于值类型,类型方法使用static标记 */
static func xxx() {
}
}
Monster.swift
class Monster {
// static 属性无法被子类覆盖
static let isTerrifying = true
// class 属性可被子类覆盖
class var spookyNoise: String {
return "Grrr"
}
var town: Town?
var name: String
var victimPool: Int {
get {
return town?.population ?? 0
}
set {
town?.population = newValue
}
}
//子类必须得实现的初始化方法
required init(town: Town?, monsterName: String) {
self.town = town
name = monsterName
}
func terrorizeTown() {
if town != nil {
print("\(name) is terrorizing a town!")
} else {
print("\(name) hasn't found a town to terrorize yet...")
}
}
/* 对于类,类型方法使用class或者static标记,区别在于static标记的方法无法被子类重写,也可以使用final class代替static */
static func makeSpookyNoise() {
print("Brains...")
}
}
Zombie.swift
class Zombie: Monster {
override class var spookyNoise: String {
return "Brains..."
}
var walkWithLimp: Bool
//只将 set 属性设置为私有,get 属性为默认的 internal
private(set) var isFallingApart: Bool
//指定初始化方法
init(limp: Bool, fallingApart: Bool, town: Town?, monsterName: String) {
walkWithLimp = limp
isFallingApart = fallingApart
//如果有父类,子类的指定初始化方法必须调用父类的指定初始化方法
super.init(town: town, monsterName: monsterName)
}
//便捷初始化方法,必须调用指定初始化方法或者其他便捷初始化方法,但最终都要调用指定初始化方法,以确保所有属性都进行了初始化
convenience init(limp: Bool, fallingApart: Bool) {
self.init(limp: limp, fallingApart: fallingApart, town: nil, monsterName: "Fred")
if walkWithLimp {
print("This zombie has a bad knee.")
}
}
required init(town: Town?, monsterName: String) {
walkWithLimp = false
isFallingApart = false
super.init(town: town, monsterName: monsterName)
}
//实例被清除出内存时会触发反初始化
deinit {
print("Zombie named \(name) is no longer with use.")
}
//final 禁止子类重写该方法
final override func terrorizeTown() {
if !isFallingApart {
town?.changePopulation(by: -10)
}
super.terrorizeTown()
}
}
测试代码
var town = Town(region: "West", population: 10, stoplights: 6)
town?.printDescription()
for _ in 0..<3 {
if let townSize = town?.townSize, let townName = town?.name {
print(townSize)
print(townName)
}
town?.changePopulation(by: 1_000_000)
}
//let genericMonster = Monster()
//genericMonster.town = town
//genericMonster.terrorizeTown()
var fredTheZombie: Zombie? = Zombie(limp: false, fallingApart: false, town: town, monsterName: "Fred")
//fredTheZombie.terrorizeTown()
//fredTheZombie.town?.printDescription()
//Monster.makeSpookyNoise()
print("Victim pool: \(fredTheZombie?.victimPool)")
fredTheZombie?.victimPool = 500
fredTheZombie = nil
print(Monster.spookyNoise)
print(Zombie.spookyNoise)
//初始化数组的3种方式,本人习惯上倾向使用第一种
//使用 var 就是可变数组,let就是不可变数组
var bucketList: Array = []
var bucketList2:[String] = ["haha"]
var arr = [Int]()
arr.append(1)
arr.append(3)
arr.append(2)
//print("bucketList.count=\(bucketList.count) arr[0]=\(arr[0])")
//数组排序
let sortedArray = arr.sorted(by: {a, b in
a < b
})
print(sortedArray)
//注:只有Array 才有 joined 方法,如果元素类型不是 String,得先通过 map 方法转一次
let joinedString = arr.map { i in
String(i)
}.joined(separator: ", ")
print("[\(joinedString)]")
//字典也有多种初始化方式,这里只列出其中一种
//使用 var 就是可变字典,let就是不可变字典
var dict: Dictionary = [:]
dict["one"] = 1
dict["two"] = 2
dict["three"] = 3
//遍历字典
for (key, value) in dict {
print("\(key) = \(value)")
}
dict.removeValue(forKey: "one")
dict["two"] = nil //将值设置为nil来删除键值对
print(dict)