swift 的变量都是集成结构体,并且结构体里边和实现了很多的方法
swift 不允许基本属性为空(必须有一个不为空的初始值),所以引入了可选类型
?
:可选类型
就是一个盒子,里边装这数据
!
:强制解包
脱掉盒子,拿到里边的数据
if
或者guard
可选项绑定解包
let a : Int? = 10
if let b = a {
// 如果a 解包 == b
print(b)
}else {
//解包失败
}
// guard 默认是flase(和if 相反)
guard let b = a else{
// 如果a 解包 == b
print(b)
}
??
:空合运算符
// 底层是一个自动闭包
//如果b不为空,把b赋值给a,如果b为空,把c赋值给a
let a = b ?? c
//> 注意:b 必须是可选类型
// b和c的存储类型必须相同
// a的类型取决于c 的类型,如果c 为可选类型,那么a为可选,如果c为值,a也为值
let b : Int? = 10
let c = 1
let a = b ?? c // a = 10 强制b解包,赋值给a
let b : Int? = nil
let c = 1
let a = b ?? c // a = 1
//空合运算符解包(解包成功就赋值,否则赋值为o)
let a : Int? = 10
let b = a ?? 0
枚举:
// 普通
enum Direction {
case north //成员值
case south
case east
case west
}
// a(enun) 一个字节保存(0,1,2,3)
let a = .north
enum Score {
case points(Int) // 关联值 (成员值和其他类型的关联存储在一起)
case grade(String)
}
// b(enun) 16个字节保存(int = 8 string = 8)
// 关联属性保存在enum 内存中
let b = Score.points(98)
enum Poker : String {//原始值类型
case a = "A" // 原始值(相同类型的默认值赋值给关联值(默认关联成员值))
case b = "B"
}
// c(enun) 1个字节保存(1,2)
// 原始值其实就是一个计算属性,内部是以set来实现的,所以不会占enum 的内存
let c = Poker.a
let d = Poker.a.rawValue//c.rawValue
结构体
struct Point {
var x : Int
var y : Int
}
// 结构体默认有一个init 方法
// a (struct) 占 16 字节
var a = Point(x : 10, y : 20)
//下边的方法不行(变量必须包含一个初始值)
var a = Point(x : 10)
var a = Point(y : 20)
var a = Point()
struct Point {
var x : Int = 10
var y : Int = 20
}
// 结构体默认有4个init 方法
//下边的方法都可以
var a = Point(x : 10, y : 20)
var a = Point(x : 10)
var a = Point(y : 20)
var a = Point()
// 下边2中写法相同(系统默认添加init初始化器)
struct Point {
var x : Int = 10
var y : Int = 20
}
struct Point {
var x : Int
var y : Int
init(){
x = 10
y = 20
}
}
类
class Point {
var x : Int = 10
var y : Int = 20
}
// 如果类有初始化值就,默认创建一个init方法
// 如果没有初始值,默认一个init 方法都没有
// a (class) 占 32 字节,保存在堆上 前8个字节保存指向类型的信息,后8个字节是引用计数,再后边是x 和 y 各占8字节
var a = Point()
//下边的方法不行(类没有)
var a = Point(x : 10)
var a = Point(y : 20)
var a = Point(x : 10, y : 20)
类和结构体的区别
- 结构体(枚举) 是值类型 而 类是引用类型(指针类型)
- 值类型保存在栈里边,而引用类型保存在堆里边
- 值类型赋值,采用的是深拷贝(在swift 标准库中,sring,arr,dic 等如果指没有修改,采用的就是浅拷贝)
- 类可以继承,而结构体不可以
闭包表达式
func sum (_ vi: Int, _v2: Int) -> Int{
return v1 + v2
}
//等价下边这个,return 一行可以省略
func sum (_ vi: Int, _v2: Int) -> Int{ v1 + v2}
// 使用闭包表达式
let fn = {
(_ vi: Int, _v2: Int) in { v1 + v2}
}
fn(10, 20)
//闭包精简过程
func exec(v1: Int, v2:Int, fn: (Int, Int) -> Int){
print(fn(vi, v2))
}
//调用
exec(v1:10, v2:20, fn: {
(vi: Int, v2: Int) -> Int in return v1 + v2
})
exec(v1:10, v2:20, fn: {
vi, v2 in return v1 + v2
})
exec(v1:10, v2:20, fn: {
vi, v2 in v1 + v2
})
exec(v1:10, v2:20, fn: {
$0 + $1
})
//尾随闭包(当最后一个参数是闭包表达式,可以放到函数调用的外边)
exec(v1:10, v2:20) {
$0 + $1
}
闭包
当闭包表达式引用了捕获了外部变量,就把这个表达式+变量的组合叫做闭包
typealias Fn = (Int) -> Int
func getFn () -> Fn{
var a = 0
func plu(_ i: Int) -> Int{
a += i
return a
}
return plu
}//plu + a 形成闭包
//a为局部变量,保存在栈里边,fn(1) 执行完成,就释放了;那么为什么fn(2) = 3
// 闭包为了延长局部变量的生命周期,把a重栈空间拷贝到堆空间
// fn 占16字节,前8个字节是plu的地址值,后8个是a堆空间的地址值(如果没有捕获,后边都是0)
//当调用plu 的时候,会把a的地址值传进去
var fn = getFn()
print(fn(1)) // 1
print(fn(2)) // 3
var fn1 = getFn()
print(fn1(1)) // 1
print(fn1(2)) // 3
//自动闭包 @autoclosure
func geta (_ v1: Int, _ v2: @autoclosure () -> Int) ->Int{
return v1 > 0 ? v1 : v2()
}
//自动把 20 变成了闭包 :geta(10, {20})
geta(10, 20)
属性
分类:
1.实例属性:使用对象调用
存储实例属性:相当成员变量,类和结构体可以定义而枚举不可以
计算实例属性:就是方法,不占用内存,枚举也可以定义
2.类型属性:使用类调用的(在class 也可以使用class 修饰,线程安全)
存储类型属性:整个程序运行过程中只有一份内存(static 修饰)
计算类型属性:static 修饰
//一般情况下,使用计算属性就是因为计算属性和存储属性存在魔种有关系
class c {
//存储属性
var a :Double
//计算属性:不能使用let,因为要改变。
//可以只有get 方法(但是这样也不能使用let),不能只有set方法
var b : Double {
set {
a = newValue / 2
}
get {
a * 2
}
}
}
延迟属性lazy
在第一次使用的时候初始化
class text {
// lazy 不能使用let(let 必须在初始化之前有初始值)
// 多线程调用lazy 属性,有可能初始化多次
lazy var a: Int
}
var a = text()//没有初始化a, a.a 的时候调用
属性观察器
非lazy 可以添加
在初始化的时候不会触发属性观察器
class text {
var a: Int{
willSet{
print(newValue)
}
didSet{
print(oldValue, newValue)
}
}
}
存储类型属性
class Car {
static var count = 1
}
// 和全局变量一样
// 默认为lazy,并且可以为let
// 第一次赋值( count = 1)的时候,底层调用了dispath_one 初始化
Car.count = 10
方法
实例方法:对象调用的方法
类型方法: 类型调用的方法(结构体和class)
class Car {
static var cout = 0
init() {//实例方法
}
//类型方法
static func getCount() ->Int{ cout }
}
var car = Car()
Car.getCount()
struct Car {
var a = 0
//结构体(或枚举)不能在方法里边修改变量,class 可以
//add mutating 就可以了
mutating func text(){
a = 10
}
//@discardableResult 可以消除调用的时候无返回值的警告
@discardableResult mutating func text1() -> Int{
a += 1
return a
}
}
var car = Car()
car.text1()
subscript
下标
struct Car {
var a = 0.0
var b = 0.0
// 返回值(Double) 确定了 newValue 和 get 返回值的类型
subscript(index: Int) -> Double{
set {
if index == 0 {
a = newValue
}else if index == 1{
b = newValue
}
}
get{
if index == 0 {
return a
}else if index == 1{
return b
}
return 0
}
}
}
var car = Car()
car[0] = 11.1
car[1] = 22.2
print(car[0])
print(car[1])