Swift基础
本文是个比较简单的学习笔记,更详细的内容见 Swift官方文档
1、相等性比较
Swift标准库用 < 和 == 运算符定义了 >、>=、<=,所以实现 Comparable 的 < 运算符就会自动得到这些运算符的实现,实际上 Comparable 继承自 Equatable,所以 == 运算也是必须实现的,以下是示例代码
struct Point: Comparable, CustomStringConvertible {let x: Intlet y: Intvar 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 != blet c = Point(x: 2, y: 6)
let d = Point(x: 3, y: 7)
c == d
c < d
c <= d
c > d
c >= dc + dclass Person: Equatable {let name: Stringlet age: Intweak var brother: Person?init(name: String, age: Int) {self.name = nameself.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 = rhsrhs.brother = lhs
}
p1 +++ p2
p1.brother?.name
p2.brother?.name
2、日期和时间
import Foundationlet date = Date.nowlet 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..<end])
print(substr)
3、处理异常
enum Token {case number(Int)case plus
}class Lexer {enum Error: Swift.Error {case invalidCharacter(Character)}let input: Stringvar position: String.Indexinit(input: String) {self.input = inputself.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 = 0while let nextCharacter = peek() {switch nextCharacter {case "0"..."9":let digitValue = Int(String(nextCharacter))!value = 10 * value + digitValueadvance()default:return value}}return value}
}class Parser {enum Error: Swift.Error {case unexpectedEndOfInputcase invalidToken(Token)}let tokens: [Token]var position = 0init(tokens: [Token]) {self.tokens = tokens}func nextToken() -> Token? {guard position < tokens.count else {return nil}let token = tokens[position]position += 1return token}func getNumber() throws -> Int {guard let token = nextToken() else {throw Parser.Error.unexpectedEndOfInput}switch token {case .number(let value):return valuecase .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 += nextNumbercase .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 +")
4、扩展
(1)扩展类/结构体
typealias Velocity = Doubleextension 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: Stringlet model: Stringlet year: Intlet color: Stringlet nickname: Stringvar 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.kindextension 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
(2)扩展协议
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: Doublelet minutes: Double
}struct TreadmillWorkout: Exercise {let name: String = "Treadmill Workout"let caloriesBurned: Doublelet minutes: Doublelet 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<E: Exercise>(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 = 0for 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)
5、范型
struct StackIterator<T>: IteratorProtocol {
// typealias Element = Tvar stack: Stack<T>mutating func next() -> T? {return stack.pop()}
}// Sequence 是实现 for-in 循环在内部使用的协议
struct Stack<E>: Sequence {var items = [E]()// 使用 where 子句约束 Sequence 的元素类型mutating func pushAll<S: Sequence>(_ 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<U>(_ f: (E) -> U) -> Stack<U> {var mappedItems = [U]()for item in items {mappedItems.append(f(item))}return Stack<U>(items: mappedItems)}func makeIterator() -> StackIterator<E> {return StackIterator(stack: self)}
}var intStack = Stack<Int>()
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<T, U>(_ 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<T: Equatable>(_ 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<T: CustomStringConvertible, U: CustomStringConvertible>(_ 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<Int>()
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<Int>()
myOtherStack.pushAll([1, 2, 3])
myStack.pushAll(myOtherStack)
for value in myStack {print("after pushing item onto stack, got \(value)")
}
6、协议
protocol TabularDataSource {var numberOfRows: Int { get }var numberOfColumns: Int { get }func label(forColumn column: Int) -> Stringfunc 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..<dataSource.numberOfColumns {let columnLabel = dataSource.label(forColumn: i)let columnHeader = " \(columnLabel) |"firstRow += columnHeadercolumnWidths.append(columnLabel.count)}print(firstRow)for i in 0..<dataSource.numberOfRows {var out = "|"for j in 0..<dataSource.numberOfColumns {let item = dataSource.itemFor(row: i, column: j)let paddingNeeded = columnWidths[j] - item.countlet padding = repeatElement(" ", count: paddingNeeded).joined(separator: "")out += " \(padding)\(item) |"}print(out)}
}struct Person {let name: Stringlet age: Intlet yearsOfExperience: Int
}struct Department: TabularDataSource, CustomStringConvertible {let name: Stringvar people = [Person]()var description: String {return "Department (\(name))"}init(name: String) {self.name = name}mutating func add(_ person: Person) {people.append(person)}var numberOfRows: Int {return people.count}var numberOfColumns: Int {return 3}func label(forColumn column: Int) -> 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.namecase 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)
7、字符串
import Cocoaextension 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..<lastIndex])}/// 16进制字符串(可以有空格)转换成二进制数据func toData() -> Data {let hex = self.replacingOccurrences(of: " ", with: "")var arr = Array<UInt8>()for i in 0..<hex.count where i % 2 == 0 {let byteStr = hex.substring(start: i, end: i + 2)// print("byteStr = \(byteStr)")arr.append(UInt8(byteStr, radix: 16) ?? 0)}return Data(arr)}
}extension Data {/// 按照 ASCII 转换成字符串func toAscii() -> 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..<end], encoding: .ascii) {return str}return ""}/// 将指定范围的数据转换成16进制字符串/// - Parameter start: 起始索引(包含)/// - Parameter end: 结束索引(不包含)/// - Parameter withSpace: 可选参数,默认true,输出的字符串是否每两个字节之间放一个空格func subdata2hex(start: Int, end: Int, withSpace: Bool = true) -> String {let arr = Array<UInt8>(self[start..<end])let hex = arr.map({ byte -> String inreturn 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)
8、类和结构体
/** 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: Stringvar 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: Stringvar address: Stringrequired init(name: String, address: String) {self.name = nameself.address = address}
}
print(BleDevice(name: "BleDevice", address: "AA:BB:CC:DD:EE:FF"))
9、值类型和引用类型
//类是引用类型
class GreekGod {var name: Stringinit(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 // Trivialet 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: IntArrayBufferinit() {buffer = IntArrayBuffer()}func describe() {let str = buffer.storage.map { i inString(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()
10、继承
虽然是讲继承,但代码中的注释也会涉及其他知识点!例子涉及3个.swift文件
Town.swift
struct Town {let region: Stringvar 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 = regionself.population = populationnumberOfStoplights = stoplights}//委托初始化init?(population: Int, stoplights: Int) {self.init(region: "N/A", population: population, stoplights: stoplights)}enum Size {case smallcase mediumcase large}//计算属性var townSize: Size {get {print("town size")//每次调用都会打印switch self.population {case 0...10_000:return Size.smallcase 10_001...100_000:return Size.mediumdefault: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: Stringvar victimPool: Int {get {return town?.population ?? 0}set {town?.population = newValue}}//子类必须得实现的初始化方法required init(town: Town?, monsterName: String) {self.town = townname = 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 属性为默认的 internalprivate(set) var isFallingApart: Bool//指定初始化方法init(limp: Bool, fallingApart: Bool, town: Town?, monsterName: String) {walkWithLimp = limpisFallingApart = 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 = falseisFallingApart = falsesuper.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)
11、数组和字典
//初始化数组的3种方式,本人习惯上倾向使用第一种
//使用 var 就是可变数组,let就是不可变数组
var bucketList: Array<String> = []
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 ina < b
})
print(sortedArray)
//注:只有Array<String> 才有 joined 方法,如果元素类型不是 String,得先通过 map 方法转一次
let joinedString = arr.map { i inString(i)
}.joined(separator: ", ")
print("[\(joinedString)]")//字典也有多种初始化方式,这里只列出其中一种
//使用 var 就是可变字典,let就是不可变字典
var dict: Dictionary<String, Int> = [:]
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)
相关文章:

Swift基础
本文是个比较简单的学习笔记,更详细的内容见 Swift官方文档 1、相等性比较 Swift标准库用 < 和 运算符定义了 >、>、<,所以实现 Comparable 的 < 运算符就会自动得到这些运算符的实现,实际上 Comparable 继承自 Equatable&…...

基于php+thinkphp+vue的校园二手交易网站
运行环境 开发语言:PHP 数据库:MYSQL数据库 应用服务:apache服务器 使用框架:ThinkPHPvue 开发工具:VScode/Dreamweaver/PhpStorm等均可 项目简介 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发…...

SystemVerilog Assertions应用指南 第一章(1.25章节 “first_match”运算符)
任何时候使用了逻辑运算符(如“and”和“or”)的序列中指定了时间窗,就有可能出现同一个检验具有多个匹配的情况。“ first match”构造可以确保只用第一次序列匹配,而丢弃其他的匹配。当多个序列被组合在一起,其中只需时间窗内的第一次匹配来检验属性剩余的部分时,“ first ma…...

python和go执行字符串表达式
1、python/eval python里可以使用内置的eval函数,来执行一个字符串表达式的结果,字符串表达式里可以是变量、函数、运算符等 def test():return True flag False print(eval("test() and True and flag" )) 执行结果为False 2、golang/go…...

Python算法练习 10.14
leetcode 2095 删除链表的中间节点 给你一个链表的头节点 head 。删除 链表的 中间节点 ,并返回修改后的链表的头节点 head 。 长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点(下标从 0 开始),其中 ⌊x⌋ 表示小于或等于…...

云上攻防-云原生篇Docker安全系统内核版本漏洞CDK自动利用容器逃逸
文章目录 云原生-Docker安全-容器逃逸&内核漏洞云原生-Docker安全-容器逃逸&版本漏洞-CVE-2019-5736 runC容器逃逸-CVE-2020-15257 containerd逃逸 云原生-Docker安全-容器逃逸&CDK自动化 云原生-Docker安全-容器逃逸&内核漏洞 细节部分在权限提升章节会详解&…...

C# Sqlite数据库的搭建及使用技巧
C# Sqlite数据库的搭建 前言: 今天我们来学一下Sqlite的数据库的搭建,Sqlite数据库不比MySqL数据库,SQlite数据是一个比较轻量级的数据库,SQLite提供了比较多的工具集,对数据基本上不挑,什么数据都可以处理ÿ…...

gerrit代码review使用基本方法
1、repo拉取代码 repo init -u ssh://gerrit.senseauto.com/senseauto_manifest -b develop -m senseauto-config.xml --repo-urlssh://gerrit.senseauto.com:29418/senseauto_repo --repo-branchdevelop --no-repo-verify repo sync -j4 repo forall -j 4 -p -c ‘git lfs p…...

网络监控与故障排除:netstat命令的使用指南
文章目录 概述什么是 netstat 命令?netstat 命令的作用和功能netstat 命令的常见用途 安装和基本用法安装 netstat 命令netstat 命令的基本语法查看活动网络连接 查看网络接口信息查看所有网络接口信息查看指定网络接口信息网络接口状态说明 网络连接状态显示所有连…...

Blender:渲染一个简单动画
接上 Blender:对模型着色_六月的翅膀的博客-CSDN博客 目标是做一个这种视频 先添加一个曲线,作为相机轨迹 然后添加一个相机 对相机添加物体约束,跟随路径,选择曲线,然后点击动画路径 假如对相机设置跟随路径后&…...

一篇文章带你用动态规划解决股票购买时机问题
动态规划的解题步骤可以分为以下五步,大家先好好记住 1.创建dp数组以及明确dp数组下标的含义 2.制定递推公式 3.初始化 4.遍历顺序 5.验证结果 股票购买时机问题的解题核心思路 当天的收益是根据前一天持有股票还是不持有股票的状态决定的 那么很自然的我们就想…...

【设计模式】使用建造者模式组装对象并加入自定义校验
文章目录 1.前言1.1.创建对象时的痛点 2.建造者模式2.1 被建造类准备2.2.建造者类实现2.3.构建对象测试2.4.使用lombok简化建造者2.5.lombok简化建造者的缺陷 3.总结 1.前言 在我刚入行不久的时候就听说过建造者模式这种设计模式,当时只知道是用来组装对象…...

简单聊聊低代码
在数字经济迅速发展的背景下,越来越多的企业开始建立健全业务系统、应用、借助数字化工具提升管理效率,驱动业务发展,促进业绩增长。在这一过程中,和许多新技术一样,低代码(Low-code)开发被推上…...

SystemVerilog Assertions应用指南 第一章(1.27章节 “within”运算符)
“ within”构造允许在一个序列中定义另一个序列。 seq1 within seq2 这表示seq1在seq2的开始到结束的范围内发生,且序列seq2的开始匹配点必须在seq1的开始匹配点之前发生,序列seq1的结束匹配点必须在seq2的结束匹配点之前结束。属性p32检查序列s32a在信号“ start”的上升沿和…...

2023年09月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试
C/C++编程(1~8级)全部真题・点这里 Python编程(1~6级)全部真题・点这里 第1题:红与黑 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。 时间限…...

[Mono Depth/3DOD]单目3D检测基础
1. 数据增强 图像放缩和裁剪后,相机内参要做相应变化 import random def random_scale(image, calib, scale_range(0.8, 1.2)):scale random.uniform(*scale_range)width, height image.sizeimage image.resize((int(width * scale), int(height * scale)))cal…...

【Docker 内核详解】namespace 资源隔离(三):PID namespace
namespace 资源隔离(三):PID namespace 1.PID namespace 中的 init 进程2.信号与 init 进程3.挂载 proc 文件系统4.unshare() 和 setns() PID namespace 隔离非常实用,它对进程 PID 重新标号,即两个不同 namespace 下的…...

1600*C. Game On Leaves(博弈游戏树)
Problem - 1363C - Codeforces 解析: 我们将目标结点 x 当作树的根,显然,到当 x 的度为 1 的时候,此时行动的人胜利。 我们假设现在的情况为,只剩余三个点,再选择任意一个点,则对方获胜。但是两…...

Apache Ant的安装
介绍 Apache Ant是一个Java库和一个 命令行工具,可以用来构建Java应用。Ant提供了许多内置的任务(tasks),可以编译、组装、测试、运行Java应用。Ant也可以构建非Java应用,例如C、C应用。 Ant非常灵活,没有…...

考研:数学二例题--∞−∞和0⋅∞型极限
前言 本文只是例题,建议先参考具体如何做这类型例题。请到主文章中参考:https://blog.csdn.net/grd_java/article/details/132246630 ∞ − ∞ 和 0 ⋅ ∞ \infin - \infin 和 0\infin ∞−∞和0⋅∞ 例题 例1: lim x → ∞ x 2 x 2 −…...

C++算法:图中的最短环
题目 现有一个含 n 个顶点的 双向 图,每个顶点按从 0 到 n - 1 标记。图中的边由二维整数数组 edges 表示,其中 edges[i] [ui, vi] 表示顶点 ui 和 vi 之间存在一条边。每对顶点最多通过一条边连接,并且不存在与自身相连的顶点。 返回图中 …...

C++学习——类其实也是一种作用域
以下内容源于C语言中文网的学习与整理,非原创,如有侵权请告知删除。 其实也是一种作用域,每个类都会定义它自己的作用域。在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针或对象…...

Seata入门系列【4】undo_log、global_table、branch_table、lock_table字段及作用详解
1 客户端 1.1 undo_log 在AT模式中,需要在参与全局事务的数据库中,添加一个undo_log表,建表语句如下: SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for undo_log -- --…...

虚幻引擎:数据表格的C++常用API
1.将数据表格中的所有数据存到一个数组中 //参数1 // 错误提示 //参数2 // 存储的数组 TArray<FKeyInfoHeader*> array; KeyInfoDT->GetAllRows<FKeyInfoHeader>(TEXT("错误"),array); 2.获取表格中所有的行名称 TArray<FName>array; …...

Java日期格式化(DateFormat类和SimpleDateFormat类)
格式化日期表示将日期/时间格式转换为预先定义的日期/时间格式。例如将日期“Fri May 18 15:46:24 CST2016” 格式转换为 “2016-5-18 15:46:24 星期五”的格式。 在 java 中,可以使用 DateFormat 类和 SimpleDateFormat 类来格式化日期,下面详细介绍这两…...

centos 7 lamp owncloud
OwnCloud是一款开源的云存储软件,基于PHP的自建网盘。基本上是私人使用,没有用户注册功能,但是有用户添加功能,你可以无限制地添加用户,OwnCloud支持多个平台(windows,MAC,Android&a…...

屏幕亮度调节保护您的眼睛
官方下载地址: 安果移动 视频演示:屏幕亮度调节-保护您的眼睛_哔哩哔哩_bilibili 嗨,亲爱的用户,你是否有过这样的体验:夜晚安静的时刻,想要在抖音上看看热门的舞蹈、在快手上发现生活的 趣味、或是在哔…...

CentOS Linux下CMake二进制文件安装并使用Visual Studio调试
cmake安装——二进制安装(很简单,推荐!!) 1)下载二进制包。首先就是官网下载二进制安装包(我们是64位系统,就下载对应的包),这里。 例如:在/home/DOWNLOAD目录下执行,即下载二进制…...

ASP.net相关目录,相关配置文件和.后缀名解释
App_Data:用于存储应用程序的数据文件,例如数据库文件或其他本地文件。 App_Start:包含应用程序的启动配置文件,例如路由配置、日志配置等。 Content:存放应用程序的静态资源文件,如 CSS、JavaScript、图…...

一键批量转换,轻松将TS视频转为MP4视频,实现更广泛的播放和分享!
在享受精彩视频内容的同时,有时我们可能会面临一个问题:某些视频格式可能不太适合我们的播放设备或分享平台。特别是TS格式的视频,在一些情况下可能无法直接播放或上传。但是不用担心,因为我们为您提供了一款强大的视频剪辑工具&a…...