基本数据类型
-
常见的数据类型:
- 值类型(value type)
- 枚举
enum / Optional
- 结构体
Bool/Int/Float/Double/Character/String/Array/Dictionary/Set
- 引用类型(reference type):
类(class) - 整数类型
在32位的平台中Int等价于Int32、64位平台Int等价于Int64
Int8/Int16/Int32/Int64/UInt8/UInt16/UInt32/UInt64
- 浮点类型:
Float
(32位,精度只有6位)
Double
(64位,精度至少15位)
func valueFunc() {
var floatDemo: Float? = 10
var doubleDemo: Double? = 29
enum TestEnum{
case one,two,three
}
struct SomeStruct{
var age = 10
var height = 167
}
}
- 字面量,
直接赋值
,编译器可以推断其类型的内容
let bool = true
let intDecimal = 17 //十进制
let intBinary = 0b1001010 //二进制
let intHexdecimal = 0x12 //十六进制
let intOctal = 0o21 //八进制
let doubleDecimal = 125.0 //十进制 等价于1.25e2
- 类型转换
func typeToChange() {
//整数转换
let int1: UInt16 = 2_000
let int2: UInt8 = 1
let int3 = int1 + UInt16(int2)
//字面量可以直接相加,字面量本身没有明确的类型
let int4 = 123 + 12.344
}
- 元组
func tupleDemo() {
//如何创建一个404错误的元组?如何取值?
let http404Error = (404,"404 error, not found")
print("The status is \(http404Error.0)")
//如何对元组取值
var firstValue = http404Error.0
var secondValue = http404Error.1
//取到元组内容
let (status, message) = http404Error
print("status code is \(status), message:\(message)")
//忽略某个元组参数
let (careStatus, _) = http404Error
print("i only care status:\(careStatus)")
}
- 条件判定(if 后面只能是Bool类型,可以省略小括号,大括号不能省略)
func loginJudge() {
let age = 5
if age >= 22 {
print("ready to marry")
}else if age > 18{
print("just in adult")
}else{
print("just a child")
}
-
while
的使用
/*
循环
while使用
repeat...while 相当于OC中的do...while循环
*/
func whileUse() {
var num = 5
while num > 0 {
print("循环我...")
num -= 1
}
var repeatTimes = 5
repeat{
print("look here ...")
repeatTimes -= 1
}while repeatTimes > 0
}
-
for
循环使用
func forLoopUse() {
let names = ["Zhang", "Lee", "Chen", "Gong"]
//...是闭区间方法,返回闭区间
for i in 0...3 {
print(names[i])
}
//使用range遍历
let range = 1...3
for j in range {
print(names[j])
}
var a = 1
let b = 3
for j in a...b {
print(names[j])
}
for _ in 0 ..< names.count {
print("for item")
}
}
- 区间运算符
/*
区间运算符
通过创建区间运算符判定指定数的范围类型
*/
func rangeCreatAndUse() {
let range = ...10
print(range.contains(20))
print(range.contains(11))
}
/*
使用区间运算符遍历数组,区间运算符:[1...b]/[1...]/[...1]/[..<2]
*/
func rangUseInArray() {
let names = ["Zhang", "Lee", "Chen", "Gong"]
for name in names[0...3] {
print(name)
}
for name in names[...3] {
print(name)
}
for name in names[..<4] {
print(name)
}
}
- 区间类型
- ClosedRange<T> 闭区间
- Range<T> 半开区间(前闭后开)
- PartialRangeThrough<T> 部分区间
//练习:使用区间判断字符是否在字母范围,请利用区间特性
func rangeType() {
let _: ClosedRange<Int> = 1...3 //闭区间
let _: Range<Int> = 1..<3 //半开区间(前闭后开)
let _: PartialRangeThrough<Int> = ...5 //部分区间
let character: ClosedRange<Character> = "a"..."z"
print(character.contains("B"))
let charRang: ClosedRange<Character> = "\0"..."~"
print(charRang.contains("B"))
}
//从4开始累加2,不超过11,筛选出对应的数据值,可以使用stride方法来进行跳跃
func rangeContainThrough() {
let allRang:ClosedRange<Int> = 1...100
// var indexValue = 0
// switch allRang.startIndex {
// case .inRange(let preIndex):
// indexValue = preIndex
// default:break
// }
//
// print("position [0] = \(indexValue)")
for targetValue in stride(from: 4, through: 100, by: 2) {
print(targetValue)
}
}
-
Switch
语句- case/default后面不能够添加大括号
- Swift中的case不用添加break,默认会执行到下一个case到来
注意点:
1. case、default 后面至少需要跟一条语句
2. 如果不想做任何事,可以在最后添加break语句结束
func switchUse() {
let num = 1
switch num {
case 1:
print(num)
case 2:
print(num)
default:
print(num)
}
}
/*
连续执行多行case,如何操作?
可以使用fallthrough操作
*/
func mutiCaseExec() {
let num = 1
switch num {
case 1:print(num); fallthrough
case 2:print(num); fallthrough
case 3:print(num); fallthrough
case 4:print(num)
case 5:print(num)
default:break
}
}
/*
switch 自定义枚举的时候,如果保证所有的枚举都列举完成,
那么可以不必使用default来处理默认情况
*/
func associateEnumValue() {
enum Answer{case right, wrong}
let finishAnswer = Answer.right
switch finishAnswer {
case .right:print("answer result is \(finishAnswer)")
case .wrong:print("answer result is \(finishAnswer)")
}
}
/*
switch case如何使用复合类型来判定
*/
func moreTypeCase() {
let string = "Jack"
switch string {
case "hello", "Lee", "Wang": print("rich man~")
case "Jack": print("poor man~~")
default: print("Not Found")
}
}
/*
Switch
1. 使用区间Range进行值匹配
2. 使用元组进行case匹配
*/
func switchRangeCase() {
let score = 45
switch score {
case 0..<60: print("Not Ok")
case 60..<80: print("Just So So")
case 80..<90: print("Good")
case 90..<100: print("Woundful")
default:print("So bad")
}
let infoMan = (84, "Good")
switch infoMan {
case let (cScore, cLevel): print("score\(cScore), level:\(cLevel)")
case let (_, cLevel): print("level:\(cLevel)")
default:break
}
}
- 值绑定
使用let = option case 进行值绑定
/*
值绑定
使用let = option case 进行值绑定
*/
func valueBind() {
let point = (2, 0)
switch point {
case (let x, var y): print("x: \(x), y:\(y)")
case (_, var y): print("x: -, y:\(y)")
case (let x, _): print("x: \(x)")
default:break
}
}
- 条件过滤
where
/*
where 条件过滤
*/
func whereFiliterUse() {
var point = (1, 2)
switch point {
case (let x, let y) where x == y: print("y=x line in dicarl line")
case (_, let y) where y==0: print("y line in dicarl line")
case (let x, let y) where (x>0 && y>0): print("location is in first part view")
default:break
}
}
- 标签语句
outer
/*
标签语句(通过outer标签语句能够简化跳转逻辑)
outer:
*/
func outerUse() {
outer: for k in 1...4 {
for j in 8...16 {
if j == 10 {
continue outer
}
if k == 3 {
break outer
}
print("k == \(k), j=\(j)")
}
}
}
函数的定义
- 函数的隐式返回
对于只有一行的语句,可以省略返回return关键字,这种省去返回return关键字的操作叫做隐式返回.
class FuncUse {
//带返回值
static func pi() -> Double {
return 3.14;
}
//加法
static func sum(v1: Int, v2: Int) -> Int {
return v1 + v2
}
//加法简化
static func sumSlim(v1: Int, v2: Int) -> Int {v1+v2}
}
/*
返回元组,实现多值返回
*/
func tupleReturnUseTypeAlias() -> (userName: String, age: String, className: String) {
(userName: "String", age: "String", className: "String")
}
- 函数默认参数值
这里的方法参数设置与C++有点区别,
C++中参数值有限制,必须从右向左设置
对于没有设置默认值的方法参数是不能够省略的
而Swift中由于存在参数标签,所以不用按顺序进行设置
例如下面的方法调用:
Swift:self.funcDefaultParamter(age: 28)
c++: self.funcDefaultParamter("lcc",28,"workMan")
func funcDefaultParamter(name: String = "lcc", age: Int, job: String = "none") {
print("name:\(name), age:\(age), job:\(job)")
}
- 可变参数(...)
- 可变参数在取值的时候使用遍历取值
- 一个方法最多只有一个可变参数
- 紧跟可变参数后面的参数不能够省略标签
func moreParameterIngoreTag(_ numbers: Int..., eachItem: (Int)->()) -> Int {
var sum = 0
for item in numbers {
eachItem(item)
sum += item
}
return sum
}
func moreParamterUse(_ numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
- 函数重载
- 返回值类型与函数重载没有关系
- 默认参数值与函数重载一起使用的时候如果产生歧义,编译器不会报错,优先调用匹配完全一致的方法
func someFunc() {
print("source func")
}
func someFunc(v1: Int, v2: Int) {
print("change func use to sum: \(v1 + v2)")
}
func someFunc(v1: Int, v2: Int, v3 :Int = 20) {
print("more parameter count: \(v1 + v2 + v3)")
}
- 输入输出参数
inout
可以在函数内部调用外界的方法,比较典型的范例就是通过元组实现两个数字的交换
//傻子方法
func swapNum(numberOne: inout Int, numberTwo: inout Int) {
let sourceTuple = (numberOne, numberTwo)
let result = (sourceTuple.1, sourceTuple.0)
numberOne = sourceTuple.1
numberTwo = sourceTuple.0
print("交换之前:\(sourceTuple), 交换之后\(result),numberOne:\(numberOne),numberTwo:\(numberTwo)")
}
//优雅方法,使用元组直接能够交互
func swapNum(numberOne: inout Int, numberTwo: inout Int, des: String = "") {
(numberOne, numberTwo) = (numberTwo, numberOne)
}
- 内联函数
@inline
在Swift编译器
中默认是开启自动转换内联函数的功能的实质:将函数调用展开成函数体。
注意:
1. 函数体比较长的不会被转换成函数体
2. 递归调用不会转换成函数体
3. 包含动态派发不能够转换成函数体
func buildToChange() {
print("transform to me")
}
//关闭内联
@inline(never) func inlineUse() {
print("即使开启编译器优化,也不会被内联")
}
//强制内联
@inline(__always) func inlineOnAlways() {
print("即使代码段很长,也会被内联,除了动态派发的函数以及递归调用的函数外")
}
- 函数类型
函数类型包含以下两个重要组成:
- 形式参数类型
- 返回值类型
func funcTest(parameter: String...) -> (String) {
var strUnition = ""
for str in parameter {
strUnition.append(str)
}
return strUnition
}
print(self.funcTest(parameter: "leo ", " is"," the", " best", " programmer"))
- 使用函数类型作为函数的参数进行传递
-
@autoclosure
的使用,对于没有参数的函数作为参数的话可以添加@autocclosure
关键字让函数的调用更加简洁高效 - 正常函数调用,如果将函数作为参数进行传递的话,可以考虑使用
$
符获取参数内容
func printSumResult(n1: Int, n2: Int, sumAdd: @autoclosure()->(Int)) {
print(sumAdd())
}
func printSumResult(n1: Int, n2: Int, sumAdd: (Int, Int)->Int) {
print(sumAdd(n1, n2))
}
self.printSumResult(n1: 1, n2: 2, sumAdd: 3)
self.printSumResult(n1: 32, n2: 29) {$0+$1}
self.printSumResult(n1: 32, n2: 11) { (num1, num2) -> Int in num1 + num2}
- 使用函数作为返回值进行传递操作
返回值是函数类型的函数,也叫做高阶函数
func returnFunUse() -> (Int, Int) -> Int {
return {$0+$1}
}
- 类型别名
typealias
typealias LCInt = Int
typealias LCFloat = Float
func typealiasUse(age: LCInt, score: LCFloat) {
print("age:\(age), score:\(score)")
}
- 嵌套函数
在函数中嵌套一个函数。
func funcInFunc() -> ((String, Int) -> () , (Int, Int) -> (Int)) {
func result(usrName: String, age: Int){
print("I get your name:\(usrName), and your age:\(age)")
}
func countTwoSum(num1: Int, num2: Int) -> Int{num1 + num2}
return (result, countTwoSum)
}
var (funcOne, funcTwo) = self.funcInFunc()
funcOne("lcc", 25)
print("count result:\(funcTwo(24, 64))")
self.funcInFuncSignal()(1,3)
-
枚举的基本用法
- 值类型枚举
- 关联值类型
将值类型成员与其他类型的值关联在一起,可以起到关联变量值存储的作用,在使用的枚举,抽象表达某些功能的时候会变得异常方便
注意:值类型与关联值类型的空间存储区别
1. 如果枚举是基本的值类型,那么枚举在内存空间的容量是固定的,因为本质存储的是原始值,默认分配1个字节的空间内容来进行存储
2. 如果枚举是关联类型的存储,那么要根据枚举的类型来确定其内存空间的占用大小,通常选择枚举关联值最大的为存储空间,还要加上原始值的存储(1个字节),最终算出来的是其真实占用的空间大小
占用空间大小与实际内存分配的大小区别
占用空间大小是通过当前数据类型占用的空间大小累加的总和,而在实际分配过程中由于iOS系统通常会采用内存对齐的方式去分配真实的空间,通常是8个字节的整数倍。所以由于内存对齐的原因,在实际分配内存上,可能分配的空间大于实际所占用的内存空间
//以下实际占用了 32+1 = 33 个字节的空间大小,实际分配了40个字节的空间大小
enum Password {
case number(Int, Int, Int, Int)
case other
}
//值类型,仅分配1个字节存储原始值内容
enum Direction {
case top, left, bottom, right
}
//关联值类型
enum LoadServerMethod {
case LoadFirst(url: String)
case LoadSecond(url: String)
case LoadFinish(finish: (Any)->())
}
func testEnum() {
let gogoUp = Direction.top
// let requestServer = LoadServerMethod.LoadFirst(url: "http://www.baidu.com")
let finishClosure = LoadServerMethod.LoadFinish { (data) in
print("server data:\(data)")
}
switch gogoUp {
case .top: print("top")
case .left: print("left")
case .bottom: print("bottom")
case .right: print("right")
}
switch finishClosure{
case .LoadFirst(let url): print("请求的地址:\(url)")
case .LoadSecond(let url): print("请求的地址:\(url)")
case .LoadFinish(let finish): finish(["data":"nil"]);
}
}
- 枚举的用法
枚举成员可以使用相同的类型的默认值进行预先关联,默认设置的值叫做原始值,枚举原始值类型如果是Int、String,Swift
会默认分配其原始值
func enmuSourceValue() {
enum DefaultIntEnum: String{
case top, left, bottom, right
}
enum TypeSource: Int{
case age = 18
case birth = 19701010
case idCard = 00000000000
}
//打印出原始值
print(DefaultIntEnum.top.rawValue)
print(DefaultIntEnum.top)
}
- 枚举递归
- 递归枚举类似于函数的递归,是枚举内容本身调用枚举本身
- 递归枚举可以用来按层级存储关联数值,在使用的时候结合业务
- 一般使用递归函数配合来进行switch..case的判定
func indirectEnum() {
enum AirthExpr {
case number(Int)
indirect case sum(AirthExpr, AirthExpr)
indirect case difference(AirthExpr, AirthExpr)
}
func traverseEnum(airthNum: AirthExpr) -> Int {
switch airthNum {
case .number(let num): return num
case let .sum(airthExpOne, airthExpTwo): return traverseEnum(airthNum: airthExpOne) + traverseEnum(airthNum: airthExpTwo)
case let .difference(airthExpOne, airthExpTwo): return traverseEnum(airthNum: airthExpOne) - traverseEnum(airthNum: airthExpTwo)
}
}
let five = AirthExpr.number(5)
let four = AirthExpr.number(4)
let sumAdd = AirthExpr.sum(five, four)
let diffEnum = AirthExpr.difference(five, four)
print("add result :\(traverseEnum(airthNum: sumAdd))")
print("different result :\(traverseEnum(airthNum: diffEnum))")
}
- 可选类型
声明某个变量为可选,直接在变量的类型后面添加?即可
对于可选类型的变量或者常量,有两种方式可以拿到其原始值:- 强制解包
- 可选链操作
可选可以理解为一层包装盒子,盒子里装的是真实数据
- 如果为nil,那么它是一个空盒子
- 如果非nil,那么盒子装的就是被包装的数据
!是强制解包的标识符,如果对nil的可选类型进行强制解包,将会产生运行时错误(Fatal error:...)
注意:
数组返回的数值不是可选类型的数值,因为当数组越界的时候就已经发生崩溃,并不能拿到带有歧义的数值(nil或者有数值),而字典获取的数值为可选数值
func unwrappTypeUse() {
var age: Int?
//直接相加由于age是可选类型所以会报错
// age+=10
//通过!来进行强制解包
print(age!+=10)
}
- 解包之可选绑定
- 如果包含内容就自动解包,把可选内部包装的值赋值给一个临时变量 var 或者常量 let,并返回 true, 否则返回false
- 如果存在多个值,可选绑定可以通过逗号分开,注意不能使用&&分割
func unwrappingBindUse() {
let num: Int? = 10
let addTo: Int? = nil
if let tmpNum = num,
let _ = addTo{
print("含有内容:\(tmpNum)")
}
}
-
while
使用可选绑定
/*
while中使用可选绑定
遍历数组,将遇到的整数都加起来,如果遇到复数或者非数字,停止遍历
*/
func bindLetWhileLoop() {
let strs = ["10", "20", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print("运算结果:\(sum)")
}
- 合并空运算符
??
- 被修饰的是可选类型
- 合并的备选常量或者变量与被修饰的类型要保持一致
- 如果备选不是可选类型,返回的时候会自动进行解包操作
注意:
空合并运算符(??)是如何实现的?为什么在返回默认值的时候要使用闭包来实现,直接返回默认内容不可以吗?
1. 包装了一个函数里面实现的public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
2. 内部实现使用了switch判定类似于以下的操作,使用运算符重载操作
运算符重载??实现可能如下:
func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T{
switch optional {
case .some(let optional): return optional
case .none: return try!defaultValue()
}
}
- 多个空合并运算符
??
一起使用
/*
多个 ?? 一起使用
*/
func mutiDefaultValueUse() {
let a: Int? = 1
let b: Int? = 2
let c = a ?? b ?? 3 //c是Int,1
let d: Int? = nil
let e: Int? = 2
let f = d ?? e ?? 3 //f是Int, 2
}
-
??
与if let
配合使用
/*
?? 与 if let 配合使用
等同于 if a! = nil || n != nil
*/
func defaultWithChooseBind() {
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
print("result--->\(c)")
}
}
-
guard
语句
- 当
guard
语句条件为false
时,会执行大括号里面的内容,否则正常执行 -
guard
时候提前过滤不满足当前方法或者函数执行的条件 - 当使用
guard
语句进行可选绑定的时候,绑定的常量let
、变量var
也会在外层作用域使用
func guardUseStatement(age: Int, level: Int) {
guard age > 18, level > 50 else {
print("未成年人禁止浏览")
return
}
print("一起看动画片啊~")
}
- 隐式解包
- 某些情况下,有些可选类型变量一开始设定就直接有值
- 在确定可选类型内容一直有值的情况下,可以通过在变量后面添加!的方法去除类型检查
- 去除类型检查之后,每次获取的可选类型变量将会隐式进行解包取出其中包含的数值
func unwrappingValueUse() {
var num: Int! = 10
var choose: Int? = 20
print("隐式解包\(num)")
print("强制解包\(choose!)")
}
- 字符串查值
func strDesprint() {
let age: Int? = 10
print("age:\(age ?? 0)") //这样打出来的内容是可选类型
print("ageDes:\(String(describing: age))")
}
- 多重可选项
多重可选项可以理解为多重可选的包装,在取值的时候需要格外注意,要分层取值。
func moreUnwrappingValue() {
let num: Int? = 10
let userone: Int?? = 20 //被包装了两次,解包的时候需要解包两次才行
let userTwo: Int??? = 30 //道理同上
let numValue = num!
let userOneValue = userone!!
let userTwoValue = userTwo!!!
print("当前内容:\(numValue), \(userOneValue), \(userTwoValue)")
}