swift学习
一、基础部分
1. 常量(let
) 变量(var
)
swift能够自己推断变量的类型,可以不用写变量的类型
let test = "你好,世界!!"
let test:String = "你好,世界!!"
var π:Double = 3.14
2. 类型转换
如果常量和变量的类型不一致,必须把类型转成一致的以后在做运算,但是如果是字面量的话,swift会自动判断类型,所以不用转换
字面量:就是表达式右边的值,就叫字面量,
let three = 3
let pi = 3.14
let threeAndPi = (Double)three + pi (正确)
let threeAndPi = three + pi (错误)
let threeAndPi = 3 + 3.14
3. 可选类型(Optional)
为什么需要Optional,因为非可选类型不能赋值为nil
var surveryAnswer:String = nil (错误)
var surveryAnswer:String? = nil (正确)
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
基本运算符
1. 三目运算符 、空和运算符
三目运算符
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20) // rowHeight 现在是 90
空合运算符:是为了判断Optional类型为nil的时候,提供默认值,与三目运算符有区别
let defaultColorName = "red"
var useDefineColorName:String?
let defineColorName = useDefineColorName ?? defaultColorName
2. 区间运算符
闭区间
a…b
半开区间
a..
集合类型
1. 数组Arrays
创建空数组
var someInts = [Int]()
var someInts = []
var threeDoubles = Array(repeatElement(0.0, count: 3))
一次改变数组中的多个值
shoppingList[4...6] = ["Bananas", "Apples"]
2. 集合Set
创建集合
var favoriteGenres:Set<String> = ["Rock","Classical","Hip hop"]
移除元素
let rock = favoriteGenres.remove("Rock")
print(rock!)
遍历集合
for item in favoriteGenres {
print(item)
}
集合操作
• 使用 intersection(_:) 方法根据两个 合中都包含的值创建的一个新的 合。
• 使用 symmetricDifference(_:) 方法根据在一个 合中但不在两个 合中的值创建一个新的 合。 • 使用 union(_:) 方法根据两个 合的值创建一个新的 合。
• 使用 subtracting(_:) 方法根据不在该 合中的值创建一个新的 合
。
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sort()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits. intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits. symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
3. 字典 Dictionary
//创建字典
var ariports:[String:String] = ["LHR":"London"]
//修改字典
ariports.updateValue("ShangHai", forKey: "LHR")
ariports["LHR"] = "Asia"
//遍历字典
for (ariportsCode,ariportsValue) in ariports {
print(ariportsCode+":"+ariportsValue)
}
for ariportsCode in ariports.keys {
print(ariportsCode)
}
for ariportsValue in ariports.values {
print(ariportsValue)
}
4.控制流
//闭区间
for item in 1...5 {
print(item)
}
//开区间
for item in 1..<5 {
print(item)
}
//用 _ 忽略item的值
var base = 0
for _ in 1...5 {
base += 1
}
print(base) //: base = 5
//while 循环
repeat {
statements
} while condition
//switch
let someCharacter:Character = "z"
switch someCharacter {
case "c": //错误 ,case后面必须有语句
case "z":
print("The letter first of the alphabat")
case "a":
print("The letter last of the alphabat")
default:
print("some other character")
}
switch someCharacter {
case "c","z": //正确 ,
print("The letter first of the alphabat")
case "a":
print("The letter last of the alphabat")
default:
print("some other character")
}
// 可以用 _ 匹配所有可能的值
// 如果存在多个匹配,那么只会执行第一个被匹配到的 case 分支。考虑点(0, 0)会首先匹配 case (0, 0) ,因此剩下的能够匹配的分支都会被忽视掉
let anthorPoint = (0,0)
switch anthorPoint {
case (0,0):
print("origin")
case (_,0):
print("_ 0 \(anthorPoint)")
case (0,_):
print("0 _ \(anthorPoint)")
case (-2...2,-2...2):
print("-2...2 \(anthorPoint)")
default:
break
}
值绑定(Value Bindings)
case 分支允许将匹配的值绑定到一个临时的常量或变量,并且在case分支体内使用 —— 这种行为被称为值绑 定(value binding),因为匹配的值在case分支体内,与临时的常量或变量绑定。
下面的例子展示了如何在一个 (Int, Int) 类型的元组中使用值绑定来分类下图中的点(x, y):
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// 输出 "on the x-axis with an x value of 2"
Where
case 分支的模式可以使用 where 语句来判断额外的条件。
下面的例子把下图中的点(x, y)进行了分类:
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// 输出 "(1, -1) is on the line x == -y"
fallthrough 关键字不会检查它下一个将会落入执行的 case 中的匹配条件。 fallthrough 简单地使代 码继续连接到下一个 case 中的代码,这和 C 语言标准中的 switch 语句特性是一样的
// 验证API的可用性
if #available(platform name version, ..., *) {
APIs 可用,语句将执行
} else {
APIs 不可用,语句将不执行
}
函数
函数返回可以是可选类型 Optional
,当返回值为nil
的时候用可选类型
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
你可以在函数名称前指定它的参数标签,中间以空格分隔。可用(_
)忽略参数标签
func someFunction(argumentLabel paramterName:Int)->Void {
print(paramterName)
}
someFunction(argumentLabel: 2)
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错 误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那 么就应该把这个参数定义为输入输出参数(In-Out Parameters)。
func swapTwoInts(_ a: inout Int,_ b: inout Int)->Int{
let tempoary:Int = a
a = b
b = tempoary
return tempoary
}
var aa = 10
var bb = 20
print(swapTwoInts(&aa, &bb)) // : 10
闭包
()->Void 简单的闭包定义。
逃逸闭包 @escaping
,闭包作为参数传入函数的时候,如果是逃逸闭包的话此闭包是被self,引用的。也就是引用计数会加1,即使函数执行之后,self引用也不会释放。
class A {
func someMethod(cloure: @escaping()->String){
print("some:\(cloure)")
}
}
class B {
var somePerproty = “hello”
let classA = A()
func testCloure(){
classA.someMethod {
self.somePerproty = “test”
return self.somePerproty
}
print(“some:(somePerproty)”)
}
}
枚举
当不需要匹配每个枚举成员的时候,你可以提供一个 default 分支来涵盖所有未明确处理的枚举成员:
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
枚举的关联值。
- 可以让枚举值对应的原始值不是唯一的, 而是一个变量.
- 每一个枚举可以是在某种模式下的一些特定值
enum lineSegmentDescriptor{
case StartAndEndPattern(start:Double, end:Double)
case StartAndLengthPattern(start: Double, length:Double)
}
var lsd = lineSegmentDescriptor.StartAndLengthPattern(start: 0.0, length: 100.0)
lsd = lineSegmentDescriptor.StartAndEndPattern(start: 0.0, end: 50.0)
print(lsd)
// 输出结果: StartAndEndPattern(0.0, 50.0)
注意
原始值和关联值是不同的。原始值是在定义枚举时被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终不变。关联值是创建一个基于枚举成员的常量或变量时才设置的值,枚举成员的关联值可以变化。
枚举原始值隐式赋值。
在使用原始值为整数或者字符串类型的枚举时,不需要显式地为每一个枚举成员设置原始值,Swift 将会自动为 你赋值。
如果mercury没有默认值,那么就是0,随后递增,
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
字符串默认值就是字符串本身
enum CompassPoint: String {
case north, south, east, west
}
使用关键字indirect
表示枚举成员是可递归的。
类和结构体
结构体和枚举和基本的数据类型一样是’值类型’,也就是数据传递的时候,都是copy一个副本。类是引用。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
这时候修改cinema的属性,对hd不会有影响。
如果创建了一个结构体struct
的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使有属性被声明为变量也不行,但是类 class
可以修改属性,即使类被定义成常量:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // 该区间表示整数0,1,2,3
rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个变量属性,这里还是会报错
延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 lazy 来标示一个延迟存 储属性。
注意
必须将延迟存储属性声明成变量(使用 var 关键字),因为属性的初始值可能在实例构造完成之后才会得 到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
类型属性用关键字static
,如果需要在子类中重写需要用关键字class
,类型方法同理,
class ViewMode {
static var interlaced = false
var frameRate = 0.0
var name:String?
func someViewMode() {
//这里会出现编译错误,因为`interlaced` 是类型属性,而
// `someViewMode` 方法不能修改类型属性
interlaced = false
//这样写不会报错
ViewMode.interlaced = false
}
//如果需要修改`interlaced` ,需要把方法定义成类型方法
static func someViewMode1() {
interlaced = false
}
}
注意:结构体和枚举与类有一些区别。结构体和枚举是值类型。默认情况下,值类型的属性不能在它的实例方法中被修改。
struct LevelTracker {
static var highestUnlockLevel = 1
var currentLevel = 1
static func unlocked(level:Int){
if level>highestUnlockLevel {
highestUnlockLevel = level
}
}
//如果需要访问`currentLevel` 属性,需要把方法定义成`mutating`
mutating func someLevel() {
currentLevel = 0
}
static func isUnlocked(level:Int)->Bool {
return level<=highestUnlockLevel
}
func somePoperty() {
//这样写也会编译错误
LevelTracker.currentLevel = 1
//这个访问类型属性不会报错
LevelTracker.highestUnlockLevel = 0
}
}
还有要注意一下,如果结构体和枚举的实例是常量,那么就不能修改他们的属性,不论属性是常量还是变量,因为结构体和枚举都是值类型,和类不同。
let fixedPoint = Point(x: 3.0, y: 3.0) fixedPoint.moveByX(2.0, y: 3.0)
// 这里将会报告一个错误