一、struct和class区别
1、值类型和引用类型
值类型变量
值类型变量是直接包含数据,每个值类型变量都有自己的数据副本,因此对一个值类型变量操作不会影响另一个值类型变量
引用类型变量
引用类型变量存储数据的引用,引用类型的变量也就是对象,对一个引用类型变量的操作可能影响另一个变量所引用的对象
2、结构体(struct)
- 结构体是值类型
- 没有引用计数,不会因为循环引用造成内存泄漏,值类型是线程安全的
- 内存分配在栈中
- 地址存储的就是值,传递过程相当于传递了一个副本
- 自动初始化构造方法
- 不能继承
- 不能被序列化成NSData对象
- 不支持身份操作符“===”
3、类(class)
- class是引用类型
- 有引用计数,循环引用会导致内存泄漏
- 内存分配在堆中,需要开发者自行管理内存
- 地址存储的是引用,class的实例是通过引用传递的
- 需要显式初始化方法
- 可以继承
- 可以被序列化成NSData对象
- 支持身份操作符“===”
4、什么时候用struct
- 模型较小时
- 无需继承时
- 无需序列化成NSData对象时
- 无需Objective-C使用时
- 对变量不做变化时
- 对变量变化不需要传递时
二、访问控制关键字
- open:具备最高的访问权限,其修饰的类和方法,可以在任意模块中被访问和重写
- public:权限仅次于open,和open的区别是不允许其他模块进行继承和重写
- internal:默认权限,只允许在当前模块中访问,可以继承和重写,不允许在其他模块中被访问
- fileprivate:修饰的对象只允许在当前文件中被访问,例如他可以被一个文件中的class、struct、extension共同使用
- private:限制的实体只能在其定义的作用域(类、结构体、枚举、扩展)内访问
三、Swift和OC如何相互调用?
Swift调用OC代码
- 需要创建一个Target-BriBridging-Header.h的桥文件,在桥文件中导入需要调用OC代码的头文件即可
OC调用Swift代码
- 在OC中直接导入Target-Swift.h文件即可, Swift代码对OC可见需要@objc标记属性和方法
四、Swift的高阶函数
map
map函数用于对集合中的每一个元素应用一个转换闭包,然后返回一个包含转换结果的新集合。 示例:
let numbers = [1, 2, 3, 4, 5]
let newNumbers = numbers.map { $0 * 2 }
print(newNumbers) //输出[2, 4, 6, 8, 10]
filter
filter函数用于从集合中选择满足指定条件的元素,并返回一个包含满足条件的元素的新集合。 示例:
let numbers = [1, 2, 3, 4, 5]
let newNumbers = numbers.filter { $0 % 2 == 0 }
print(newNumbers) //输出[2, 4]
reduce
reduce函数用于将集合中的所有元素组成单个值,并返回该值。 示例:
let numbers = [1, 2, 3, 4, 5]
let newNumbers = numbers.reduce(0, { $0 + $1 })
print(newNumbers) //输出15
flatMap
flatMap函数用于对集合中的每一个元素应用一个转换闭包,并将结果拼接成一个新的集合。 示例:
let numbers = [[1, 2, 3], [4, 5, 6]]
let newNumbers = numbers.flatMap { $0 }
print(newNumbers) //输出[1, 2, 3, 4, 5, 6]
compactMap
compactMap函数用于对集合中的每一个元素应用一个转换闭包,并过滤掉结果中的nil值。 示例:
let numbers = ["1", "2", "3", "hello", "5"]
let newNumbers = numbers.compactMap { Int($0) }
print(newNumbers) //输出[1, 2, 3, 5]
allSatisfy
allSatisfy函数用于检查序列中的所有元素是否都满足指定条件。 示例:
let numbers = [2, 4, 6, 8, 10]
let newNumbers = numbers.allSatisfy { $0 % 2 == 0 }
print(newNumbers) //输出true
五、Swift中的存储属性和计算属性
Swift语言中的属性从行为上可以分为存储属性和计算属性两类。存储属性和计算属性的区别在于:存储属性用于描述存储值,而计算属性用于描述计算过程并获取计算结果;
存储属性
存储属性用来定义类或者结构体的某些特性,简单来说就是用变量var或者常量let存储的某些有意义的值;在创建类或者结构体的实例时,必须为所有的存储属性设置一个合适的初始值。
注意:使用var声明的age,在SIL中生成了set和get方法;而let声明的sex只生成了get方法;因此再次给sex赋值时实际是调用set方法,但是因为找不到set方法,所以无法再次赋值;
计算属性
与存储属性相比,计算属性更像是运算过程,计算属性并不存储值,而是提供get和set方法来修改和获取值。存储属性我们可以定义常量或者是变量,但是对于计算属性,必须定义为变量,并且计算属性在定义时必须包含类型;
六、Swift final关键字
- 限制类的继承:在类的定义前加上final关键字,可以防止其他类继承该类;
- 限制属性的重写:在属性的定义前加上final关键字,可以防止子类重写该属性;
- 限制方法的重写:在方法的定义前加上final关键字,可以防止子类重写该方法;
七、class和static区别
- 只有class对象才允许使用class关键字,结构体、协议、枚举只能使用static关键字;
- 定义类型存储属性,只能使用static,不允许使用class;
- class 修饰的计算属性可以被重写,static 修饰的不能被重写;
- static 可以修饰存储属性,static 修饰的存储属性称为静态变量(常量);
- static 修饰的静态方法不能被重写,class 修饰的类方法可以被重写;
- class 修饰的类方法被重写时,可以使用static 让方法变为静态方法;
- class 修饰的计算属性被重写时,可以使用static 让其变为静态属性,但它的子类就不能被重写了;
- 有一个比较特殊的是protocol。在Swift中class、struct和enum都是可以实现protocol的。那么如果我们想在protocol里定义一个类型域上的方法或者计算属性的话,应该用哪个关键字呢?答案是使用class进行定义,但是在实现时还是按照规则:在class里使用class关键字,而在struct或enum中仍然使用static,虽然在protocol中定义时使用的是class;
八、runLoop
什么是runLoop?
默认情况下,线程执行完任务就会退出,不再执行任务,我们需要采取一种方式让线程能够不断的执行任务,所以就有了runLoop。
顾名思义,runLoop就是一直不断执行的循环。
runloop实际上是一个对象,这个对象在循环中用来处理程序运行中出现的各种事件(触摸、UI刷新、定时器、Selector等,从而保持程序的持续运行,runLoop在没有事件处理时,会让线程休眠,只有在接收到事件时才会被唤醒,然后处理相应事件。
一条线程对应一个runloop对象,主线程的runloop对象由系统自创建
RunLoop Mode
- default
- tracking:界面跟踪mode,用于scrollView跟踪触摸滑动,保证界面滑动不受其他mode影响
- common
常驻线程
为当前线程添加一个runloop
向该runloop中添加一个prot/Source等维持RunLoop的事件循环
启动该RunLoop
九、Runtime
OC是一门动态语言
动态语言是指程序在运行时可以改变其结构;添加新的函数、属性,删除已有的函数、属性等结构上的变化,在运行时做类型的检查。
OC的动态性由runtime支持的。
runtime是一个C语言的库,提供API创建类,添加方法,删除方法,交换方法等。