1.class 和 struct
class 类 引用类型
- 可以被继承,子类可以使用父类的方法和属性
- 变量赋值时候,通过指针copy, 浅拷贝
- 存储在堆空间,需要考虑内存泄漏(循环引用),操作属性相关是指针操作,移动删除添加等
- 如果class 继承Object , 拥有runtime 机制, 可以在运行时阶段对类进行检测和解释
- 使用deinit析构函数释放资源(可以参考OC dealloc)
struct 结构体 值类型
- 不能够被继承
- 变量在复制的时候,直接产生一个新的副本,属于深拷贝
- 存储在栈空间,不需要考虑内存泄漏情况,使用完就回立即释放
- 方法调用时候,是直接调用,在编译期间就已经确认了方法地址
2.Swift 中 strong, weak ,unowned
- Swift 内存管理同OC管理机制一样,通过引用技术,当引用计数retaincount 为 0 的时候进行释放资源 OC 调用dealloc , Swift 调用 deinit
- 都是属性修饰关键字
- strong:强引用,修饰属性的时候,默认使用strong , 对象的引用为强引用,引用技术retaincount 加 1
- weak: 弱引用,如果对象或者属性 被weak修饰,指针引用的是是弱引用,引用计数不改变(不会自动+1),释放的时候 ,进程会自动设置为nil
- unowned:无主引用, 同weak 使用方式和功能一样,唯一不同的是,在释放的时候对象销毁的时候,不会自动设置nil, 会产生野指针,再次访问该对象会出现崩溃,一般用在该对象不会销毁情况,解决循环引用问题
- weak、unowned 都用在解决循环引用问题,unowned效率高一些,很多公司统一使用 weak (包括我在内)
3.copy on write 写时复制
这个针对值类型数据生效,值类型在复制对象与源对象内存指针指向的是同一个内存地址,只有当修改对象的时候,才会在内存中重新创建一个新对象
- 系统标准库提供:Struct、String、Array、Dictionary、Set 都是用Copy on write技术、这样可以提升性能,存储在栈空间。
- 只有在写操作的时候,先对原始数据复制一个副本,然后再修改副本
原理:
源码在 OptimizationTips.rst 里发现如下代码, 使用泛型
final class Ref<T> {
var val : T
init(_ v : T) {val = v}
}
struct Box<T> {
var ref : Ref<T>
init(_ x : T) { ref = Ref(x) }
var value: T {
get { return ref.val }
set {
if (!isKnownUniquelyReferenced(&ref)) {
ref = Ref(newValue)
return
}
ref.val = newValue
}
}
}
isKnownUniquelyReferenced , 用来检查实例是不是唯一引用
在属性赋值的时候,调用set方法,里面判读是否有多个 reference ,如果有多个就进行拷贝,没有则不会
4.Swift 标准库 Array,String,Dictionary,Set,设计为值类型
- 值类型跟引用类型对比
- 值类型可以高效的使用内存,效率比引用类型高
- 值类型是在栈上操作使用完就回被释放掉,引用类型在堆上操作引用计数为0释放
- 栈操作只涉及到了指针的移动,堆操作设计指针移动,合并,重链接
5.Swift高阶函数 map
、flatMap
、compactMap
、filter
、reduce
。
map
数组中的每一个元素调用一次闭包函数,并返回该元素所映射的值
let array = [8,9,10,11]
//修改数字里面内容
let tmp1 = array.map{_ in 2}
//数组内数字都转换为指定字符串
let tmp2 = array.map{_ in "$0"}
// 数组内数字 * 2之后 转换为字符串
let strs = array.map{"\($0 * 2)"}
//生成二位数组,每个数字重复5次
let maps = array.map { Array(repeating: $0, count: 5) }
print("tmp1 = \(tmp1)")
print("tmp2 = \(tmp2)")
print("strs = \(strs)")
print("maps = \(maps)")
//输出结果
tmp1 = [2, 2, 2, 2]
tmp2 = ["$0", "$0", "$0", "$0"]
strs = ["16", "18", "20", "22"]
maps = [[8, 8, 8, 8, 8], [9, 9, 9, 9, 9], [10, 10, 10, 10, 10], [11, 11, 11, 11, 11]]
flatMap
与map类似,数组中的每个元素调用闭包中的规则,返回一个新的数组
flapmap 会把数组中的多维数组进行降维操作,都会变成一维数组,可以过滤元素 nil
let array1 = [[8,9],[10,11],[8,9,10,11]]
let mapNums = array1.flatMap { $0 }
print("mapNums = \(mapNums)")
//输出结果
mapNums = [8, 9, 10, 11, 8, 9, 10, 11]
compactMap
compactMap 在 Swift 4.1 之后对flapmap 的一个重载方法的重命名,同样数组每个元素都要执行一下闭包里面个规则,返回一个新的数组,不同之处在于会出去规则之后的 nil 值,对可以选项进行解包操作
let array2 = ["2","3","c","5"]
let map2 = array2.map { Int($0) }
let flapmap2 = array2.flatMap{ Int($0) }
let compactMap2 = array2.compactMap { Int($0) }
print("map2 = \(map2)")
print("flapmap2 = \(flapmap2)")
print("compactMap2 = \(compactMap2)")
//输出结果
map2 = [Optional(2), Optional(3), nil, Optional(5)]
flapmap2 = [2, 3, 5]
compactMap2 = [2, 3, 5]
filter
过滤数组中元素,筛选出满足闭包中条件元素
let array3 = ["2","3","c","5"]
let res = array3.filter{$0 == "2"}
print("res = \(res)")
//输出结果
res = ["2"]
reduce
对集合中每个元素和叠加器做对应操作,根据闭包中规则整合数组中的数据
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
initialResult : 初始值, (Result($0), Element) Element 需要处理的元素,Result 处理后返回给Result 作为下次闭包的参数
简要概述: initialResult 作为起始值, 然后执行闭包里面规则,运算完之后 把结果返回,作为下次参数执行闭包的参数,然后遍历整个数组
let res1 = array.reduce(2, +)
print("res1 = \(res1)")
let res2 = array.reduce(2) { result, num in
print("result2 = \(result) -- num2 = \(num)")
return result + num
}
print("res2 = \(res2)")
let res3 = array.reduce(2){
print("result3 = \($0) -- num3 = \($1)")
return $0 + $1
}
print("res3 = \(res3)")
// 输出结果
res1 = 40
result2 = 2 -- num2 = 8
result2 = 10 -- num2 = 9
result2 = 19 -- num2 = 10
result2 = 29 -- num2 = 11
res2 = 40
result3 = 2 -- num3 = 8
result3 = 10 -- num3 = 9
result3 = 19 -- num3 = 10
result3 = 29 -- num3 = 11
res3 = 40
6.defer 使用场景
使用defer 代码块,在函数方法执行完之前,执行defer中代码,或者抛出错误也需要执行一些操作,相当于延时操作,一般用来做资源释放和销毁使用
在同一个函数中,如果有多个defer语句要执行,执行顺序是倒序的
class Car {
let array = [1,2,3,4]
init(){}
func testDefer(){
print("开始")
defer{
print("defer1")
}
defer{
print("defer2")
}
defer{
print("defer3")
}
print("结束")
}
}
let car1 = Car()
car1.testDefer()
//输出
开始
结束
defer3
defer2
defer1
上面说函数退出之前执行不是绝对的
[!IMPORTANT]
A
defer
statement is used for executing code just before transferring program control outside of the scope that the defer statement appears in.
这段大概意思是说,在当前作用域退出的时候会执行defer中代码
class Car {
let array = [1,2,3,4]
init(){}
func testDefer(){
print("开始")
defer{
print("defer1")
}
defer{
print("defer2")
}
//每次作用域执行完,执行defer
for _ in 0 ..< 3{
print("循环开始")
defer{
print("defer3\n")
}
print("循环结束")
}
if array[3] == 4{
print("条件满足开始")
defer{
print("defer4")
}
print("条件满足结束")
}
print("结束")
}
}
let car1 = Car()
car1.testDefer()
//输出
开始
循环开始
循环结束
defer3
循环开始
循环结束
defer3
循环开始
循环结束
defer3
条件满足开始
条件满足结束
defer4
结束
defer2
defer1
7.String 、NString 两者区别
关系
String
: Swift中原生字符串类型,值类型NSString
:Foundation
中 Objective-C 类型,引用类型
互相转换
-
NSString 转换为 String
-
使用
as String
进行转换:let nsString: NSString = "Hello" let swiftString: String = nsString as String
-
使用构造函数进行转换
let nsString: NSString = "Hello" let swiftString: String = String(nsString)
-
-
String 转换为 NSString
-
使用
as NSString
或者String
的构造函数进行转换let swiftString: String = "Hello" let nsString: NSString = swiftString as NSString
-
使用构造函数进行转换
let swiftString: String = "Hello" let nsString: NSString = NSString(string: swiftString)
-
不同点
String
值类型,在传递和赋值时会进行复制,NSString
是引用类型,通过引用传递和赋值String
支持原生操作,例如 遍历、拼接、查找、替换等。NSString
需要使用OC方法String
是 Unicode 兼容的,它的字符编码采用 UTF-8,而NSString
的字符编码采用 UTF-16。- 各自Api不同
8.self 与 Self
- self
self
关键字,用在当前实例或者类型实例,可以使用self
访问当前实例属性、方法、或者下标,在闭包中要使用self
显示的引用
- Self
Self
是一个关联类型标识符,用于泛型约束中。它表示实际使用了该泛型类型的类型。当定义泛型时,可以使用 Self
关键字来指代当前使用泛型的类型本身。
protocol MyProtocol {
static func create() -> Self
}
struct MyStruct: MyProtocol {
static func create() -> MyStruct {
return MyStruct()
}
}
9.subscript 为 类 、结构体 添加下标
使用subscript
语法添加下标,可以用在实例对象和类实例中
class Person {
var body = ["eye","arm","hair","nose"]
init() {
}
subscript(index : Int) -> String{
set(val){
body[index] = val
}
get{
return body[index]
}
}
//类添加下标方法
class subscript(index : Int) -> String{
set(val){
jobs[index] = val
}
get{
return jobs[index]
}
}
}
let p = Person()
p[1] = "eyes"
print("p[0] = \(p[0])")
print("p[1] = \(p[1])")
//输出
p[0] = eye
p[1] = eyes
10.延迟存储属性Lazy
使用Lazy
修饰的存储属性必须使用 var
,因为let
需要初始化,在第一次访问的时候才被赋值,并不能保证线程安全, 延迟初始化可以提高性能,因为它避免了在对象初始化时不必要的计算。
class SomeClass {
init() {
}
lazy var nameLabel: UILabel = {
let lb = UILabel()
lb.text = "张三"
print("初始化label")
return lb
}()
}
let someCls = SomeClass()
print("第一次访问 = \(someCls.nameLabel.text)")
print("第二次访问 = \(someCls.nameLabel.text)")
//输出
初始化label
第一次访问 = Optional("张三")
第二次访问 = Optional("张三")