for-in循环
- i默认值是let,有需要时可以声明为var
//在一次循环中,若没有指定i为var,则不可以对i进行赋值等操作
for var i in 1...3 {
i += 5
print(i)
}
- 单侧区间与数组结合
for name in names[2...] {
print(name)
}// Brain Jack
for name in names[...2] {
print(name)
}// Anna Alex Brian
- case判断语句后可加where 来限制判断条件
switch
- fallthtough贯穿效果
- 绑定值
switch point {
case (let x ,0):
print("on the x-axis with a \(x) value")
case (0 , let y)
print("on the y-axis with a \(y) value")
case let (x,y)
print("somewhere else at (\(x),\(y))")
}//必要时let也可以改为var
函数
- 隐式返回(函数体是单一表达式,则函数会隐式返回这个表达式)
v1 + v2
}
sum(v1: 20, v2: 30)//50
-
利用元组可实现多返回值
-
函数的文档注释
///求和【概述】
///
///将2个整数相加【更详尽的描述】
///
/// - Parameter v1: 第一个数
/// - Prarmeter v2: 第二个数
/// - Returns:2个整数的和
///
/// - Note:传入2个整数即可【批注】
///
- 默认参数值
在c++等语言中,如果函数要设置默认参数值,规定必须从右向左设置默认参数值,如果不这样的话,在函数调用的过程中可能会出现参数值传递混乱的问题。Swift通过设置外部参数名(参数标签)比较成功的避免了这个问题,但是在一些特定情况下(比如,省略参数标签)也会遇到问题参数值传递混乱的问题。
func test(_ first: Int = 10, middle: Int, _ last: Int = 30){
}
test(middle:20)
//这里第二个参数不能省略参数标签,否则编译器会报错,会将20赋值给第一个参数
- 可变参数。一个函数最多只能有一个可变参数,紧跟在可变参数后面的参数不能省略参数标签
func test(_ numbers: Int..., string: String, _ other: String) {
}
test(10,20,30, string: "Jack", "Rose")
- print函数
public func print(_ items: Any..., separator: String = "", trminator: String = "\n")
print(1,2,3,4,5, separator: "_") // 1_2_3_4_5
输入输出参数(in-out paramet)
用inout定义一个参数,可以在函数内部修改外部变量的值
注 1.可变参数不能标记为inout
2.inout参数不能由默认值
3.inout参数只能传入可以被多次赋值的
4.inout参数的本质是地址传递(引用传递)
func swapValues(_ v1: inout Int, _v2: inout Int) {
(v1,v2) = (v2,v1)
}
函数重载
函数重载要求函数名相同,参数个数不同||参数类型不同||参数标签不同
注:1.返回值类型与函数重载无关,不能凭借函数返回值不同而构造函数重载
2.默认参数值与函数虫灾一起使用产生二义性时,编译器并不会报错
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func sum(v1: Int, v2: Int, v3: Int = 10) {
v1 + v2 + v3
}
//会调用sum(v1: Int, v2: Int)
sum(v1: 10, v2: 20)
3.可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器有可能会报错
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func sum(v1: Int, _ v2: Int) -> Int {
v1 + v2
}
func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
return total
}
}
// error: ambiguous ues of 'sum'
sum(10,20)
typealias
- typealias用来给类型起别名
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64
枚举的关联值
枚举类型通常有原始值,又是也会将枚举的成员变量与其他关联的值关联存储起来。一般来说,一个枚举的实例只占一个字节的内存空间,如果关联了其他类型的值,那么该枚举实例占用的内存大小为:占用内存空间最大的成员变量的字节大小+1个字节(无关联值的枚举实例的大小)。注意,原始值不占用枚举变量的内存。
MemoryLayout.size //实际用到的内存大小
MemoryLayout.stride //分配的内存大小
MemoryLayout.alignment //内存对齐方式
查看内存内容:Debug -> Debug Workflow -> View Memory -> 内存地址 if let并列解包多个可选值:if let a = optional(objcet1), let b = objectionable(object2) { } 前提:a是可选值,b是可选值或者不是可选值,但是b的存储类型必须和a相同 结果:如果a不是nil,则返回a 如果a是nil,则返回b 如果b不是可选值,那么返回a时会自动解包 ??与if let配合使用 当guard语句的条件为false时,就会执行else括号中的代码;当guard语句的条件为true时,就会跳过guard语句;guard语句特别适合用来”提前退出“。当使用guard语句进行可选值绑定时,绑定的常量、变量也能在外层作用域中使用。 定义:一个函数和它所捕获的变量/常量环境组合起来,成为闭包。一般指定义在函数内部的函数,通常情况下捕获的是外层函数的局部变量/常量。 可以把闭包想象成是一个类的实例对象,内存在堆空间,捕获的局部变量/常量就是对象的成员(存储属性),组成闭包的函数就是类内部定义的方法。 属性分为存储属性和计算属性 *** 类似于成员变量的概念 存储在实例的内存中 结构体、类可以定义存储属性 枚举不可以定义存储属性 本质就是方法(函数) 不占用实例的内存 枚举、结构体、类都可以定义计算属性 枚举rawVaule的原理:枚举的原始值其实就是只读计算属性 使用lazy可以定义一个延迟存储属性,在第一次用到属性的时候才会进行初始化 lazy属性必须是var,不能是let,let必须在实例的初始化方法完成之前就拥有值;如果多条线程同时第一次访问lazy属性,无法保证属性值被初始化一次(非线程安全)。 当结构体包含一个延迟存储属性时,只有var才能访问延迟存储属性,因为延迟属性初始化时需要改变结构体的内存。 在开发中,可以为非lazy的var存储属性设置属性观察器 总结 如果实参有物理内存地址,且没有设置属性观察器,那么inout直接将实参的内存地址传入函数(实参进行引用传递) 如果实参是计算属性或者设置了属性观察器 ,那么inout则采取Copy In Copy Out的做法 1.调用该函数时,先复制实参的值,产生副本【get】 2.将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值 3.函数返回后,再将副本的值覆盖实参的值【set】 inout的本质就是引用传递(地址传递) 严格来说,属性可以分为: 1、实例属性(Instance Property):只能通过实例去访问 2.类型属性(Type Property):只能通过类型去访问 注:1.不同于存储实例属性,必须给存储类型属性设定初始值,因为类型没有像实例那样的init初始化器来初始化存储属性。 2.存储类型属性默认就是lazy,在第一次使用时初始化,就算被多个线程同时访问,保证只会初始化一次,存储类类型可以是let 3.枚举类型也可以定义类型属性 实例方法(Instance Method):通过实例对象调用 类型方法(Type Method):通过类型调用,用static或者class关键字定义 结构体和枚举是值类型,默认情况下,值类型的属性不能被自身的实例方法修改。想要修改的话需要在func关键字前加mutating。 在func前面加@discardableResult,可以消除函数调用后返回值未被使用的警告。enum Passwod {
case number(Int, Int, Int, Int)
case other
}
MemoryLayout<object>.size //33 = 32 + 1
MemoryLayout<object>.stride //40(对齐方式是8,(33/8 +1)*8 = 40)
MemoryLayout<object>.alignment //8
可选值解包
if let c = a, let d = b {
print(c,d)
}
// <=> if a != nil && b != nil
空合并运算符 a ?? b
let a: Int? = 1
let b: Int? = 2
let c = a ?? b //c是Int?,Optional(1)
let a: Int? = nil
let b: Int? = 2
let c = a ?? b //c是Int?,Optional(2)
let a: Int? = nil
let b: Int? = nil
let c = a ?? b //c是Int?,nil
let a: Int? = 1
let b: Int = 2
let c = a ?? b //c是Int,1
let a: Int? = nil
let b: Int = 2
let c = a ?? b //c是Int,2
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
print(c)
}
//只要a ?? b不是nil就可以执行print(c),也就相当于if a != nil || b != nil
guard语句
guard ... else {
//code
//return break contiune throw error
}
结构体内存结构
//编译器会为所有结构体自动生成初始化器,有时会根据情况生成多个初始化器,其宗旨是:保证所有成员都有初始值。
struct Point {
var x: Int = 0
var y: Int = 0
var origin: Bool = false
}
MemoryLayout<Point>.size //17
MemoryLayout<Point>.stride //24
MemoryLayout<Point>.alignment //8
闭包表达式
{
(参数列表) -> 返回值类型 in
函数体代码
}
闭包(Closure)
typealias Fn = (Int) -> (Int, Int)
func getFns() -> (Fn, Fn) {
var num1 = 0
var num2 = 0
func plus(_ i: Int) -> (Int, Int) {
num1 += i
num2 += i << 1
return (num1, num2)
}
func minus(_ i: Int) -> (Int, Int) {
num1 -= i
num2 -= i << 1
return (num1, num2)
}
return (plus, minus)
}
let (p, m) = getFns()
p(5) //(5, 10)
m(4) //(1, 2)
p(3) //(4, 8)
m(2) //(2, 4)
属性
存储属性
计算属性
计算属性:
1.set传入的新值默认叫做newValue,也可以自定义
2.可定义只读计算属性,定义计算属性时只能用var,不能用let
3.计算属性的值是可能发生变化的(即使是只读计算属性),而let代表常量,所定义的值是一成不变的
**************************************************
struct Circle {
var radius: Double
var diameter: Double {
set(newDiameter) {
radius = newDiameter / 2
}
get {
radius * 2
}
}
}
只读计算属性
struct Circle {
var radius: Double
var diameter: Double {
get {
radius * 2
}
}
}
延迟存储属性(Lazy Stored Property)
class Car {
init() {
print("Car init!")
}
func run() {
print("Car is running!")
}
}
class Person {
lazy var car = Car()
init() {
print("Person init!")
}
func goOut() {
car.run()
}
}
let p = Person()
print("---------")
p.goOut()
//打印结果:
Person init!
----------
Car init!
Car is running!
属性观察器
struct Circle {
var radius: Double {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet",oldValue, radius)
}
}
init() {
self.radius = 1.0
print("Circle init!")
}
}
_ 没有弄清楚为什么计算属性不可以设置属性观察器!!
inout再次探究
struct Shape {
var width: Int
var side: Int {
willSet {
print("willSetSide", newValue)
}
didSet {
print("didSetSide",oldValue, side)
}
}
var girth: Int {
set {
width = newValue / side
print("setGirth", newValue)
}
get {
print("getGirth")
return width * side
}
}
func show() {
print("width=\(width), side=\(side), girth=\(girth)")
}
}
func test(_ num: inout Int) {
num = 20
}
var s = Shape(width: 10, side: 4)
test(&s.width)
s.show()
print("------------")
test(&s.side)
s.show()
print("------------")
test(&s.girth)
s.show()
//下面是打印结果
getGirth
width=20, side=4, girth=80
------------
willSetSide 20
didSetSide 4 20
getGirth
width=20, side=20, girth=400
------------
getGirth
setGirth 20
getGirth
width=1, side=20, girth=20
类型属性(Type Property)
struct Car {
static var count: Int = 0
init() {
Car.count += 1
}
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.count) //3
方法