项目代码CR:
1、dateFormatter优化,避免多次创建(性能问题)
-
在MyweatherViewController中创建新的属性,不用每次创建一个dateFormatter
var dateFormatter = DateFormatter()
#在第一次使用的时候
2、 self?
(弱引用表原理)
-
为什么使用弱引用
-
当你在一个函数或闭包中使用
self
时,通常创建了一个对当前实例的强引用。如果这个引用是闭包中唯一的引用,那么它将永远不会被释放,因为闭包一直引用着self
,而self
又引用着闭包,从而形成循环强引用。这会导致内存泄漏。
-
-
self?
为什么修改它?-
弱引用只指向sideTable,不再指向原来的对象。一旦强引用计数为0时,可以彻底释放原来对象的空间。如果每次去访问sideTable取值麻烦.
-
使用
self?
意味着你期望self
可能是nil
。如果self
为nil
,那么使用self
的属性或方法将导致运行时错误。
-
-
修改:
guard let `self` = self else { return } #后面直接使用self
3、函数大小(代码规范)
-
createview方法中将组建初始化设置放到componentStyle方法里边
4、switch case
优化(代码复杂度)
-
如果情况复杂的话使用.plist来代替
5、属性懒加载、类的常量初始化(代码规范)
-
什么是懒加载:
-
swift属性声明延时加载,延时加载简单来讲就是在对象被实例化的时候属性不会被赋值,只有当被用到某个属性的时候才会进行赋值。
-
为什么这么做:
-
懒加载是一种编程的优化方式。部分实例对象在被创建的时候可能需要从文本中读取数据,这个过程是一个是一个耗时的过程,用户不是每次都需要去用到这个属性。因此采用懒加载进行文件读取可以有效的加快对象的构造速度,并且在一定程度上节省内存的使用。
-
lazy var 定义。
-
-
6、闭包解决同步数据返回问题,采用异步(性能安全)
-
采用异步,比如要请求数据的话是很耗时的操作。同步和异步的操作
let semaphore = DispatchSemaphore(value: 0) requestService = RequestService(city: cityName, cityZh: cityZh) requestService?.block = { [weak self] data in self?.weatherData = data semaphore.signal() } semaphore.wait()
7、缓存:哪几种方式?沙盒机制?除了plist还有什么方案?各种方案优缺点?(性能安全)
-
缓存实现的方式有
-
Documents Directory:用于存储需要持久保存的数据,如用户数据文件。
-
Library Directory:用于存储配置文件、偏好设置等。
-
Caches Directory:用于存储临时数据,如缓存图片或下载的数据,系统可能会在需要时清除这些数据。
使用 FileManager
获取沙盒目录的路径:
import Foundation let fileManager = FileManager.default let documentsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! let libraryPath = fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first! let cachesPath = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
在沙盒目录中读写文件:
let filePath = documentsPath.appendingPathComponent("example.txt") // 写入文件 if let dataToWrite = "Hello, World!".data(using: .utf8) { try? dataToWrite.write(to: filePath) } // 读取文件 if let data = try? Data(contentsOf: filePath) { let content = String(data: data, encoding: .utf8) print(content) }
userdefault
用于存储轻量级的数据,如用户偏好设置。
let defaults = UserDefaults.standard // 存储数据 defaults.set("userValue", forKey: "userKey") // 读取数据 if let value = defaults.string(forKey: "userKey") { print(value) }
-
除了plist文件,还有json文件可以存储,数据库文件。
-
json实现:序列化:将 JSON 数据转换为
Data
类型,这通常通过JSONSerialization
或JSONEncoder
来实现。
import Foundation // 假设你有一个 JSON 对象 let jsonObject: [String: Any] = [ "name": "John", "age": 30, "isEmployee": true ] // 序列化 JSON do { let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: []) // 获取文件保存路径 let fileManager = FileManager.default guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return } let filePath = documentsDirectory.appendingPathComponent("data.json") // 写入文件 try jsonData.write(to: filePath) print("JSON data is saved successfully.") } catch { print("Error saving JSON data: \(error)") } // 获取文件路径 let fileManager = FileManager.default guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return } let filePath = documentsDirectory.appendingPathComponent("data.json") // 确保文件存在 if fileManager.fileExists(atPath: filePath.path) { do { // 读取数据 let jsonData = try Data(contentsOf: filePath) // 反序列化 JSON let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) // 将 JSON 对象转换为字典 if let dictionary = jsonObject as? [String: Any] { print("Name: \(dictionary["name"] ?? "N/A"), Age: \(dictionary["age"] ?? 0), IsEmployee: \(dictionary["isEmployee"] ?? false)") } } catch { print("Error reading JSON data: \(error)") } } else { print("JSON file does not exist.") }
8、数组判空,isEmpty(代码安全防护)
-
数组有判断空的方法。为什么建议使用isEmpty,因为如果数组不为空的话,那么就需要去遍历整个数组,比较费时间。
9、do {} catch {}(代码规范)
-
和java中try catch类似。不一样的是,do中执行可能出现的错误的代码。
-
try用在具体某一句会出现错误的代码上。
do { let data = try PropertyListEncoder().encode(datas) guard let plistFileURL = plistFileURL else { return } try data.write(to: plistFileURL, options: .atomic) } catch { print("Failed to save array to plist: \(error)") }
-
此外,
try?
的用法。-
可选结果:
try?
会将抛出的错误转化为nil
,因此结果总是一个可选类型。 -
错误消费:使用
try?
时,错误被“消费”掉,即不会被抛出或进一步处理。这在某些情况下是有用的,但也可能隐藏错误,使调试变得困难。 -
简洁性:
try?
使得代码更简洁,特别是当错误不需要立即处理时。
-
10、IO操作,子线程异步操作防阻塞,UI相关更改刷新在主线程(性能问题)
11、属性private
、public
区分(代码规范)
-
通过修饰符
open
、public
、internal
、fileprivate
、private
来声明实体的访问级别-
open 和 public 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 open 或 public 级别来指定框架的外部接口。open 和 public 的区别在后面会提到。
-
internal 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 internal 级别。
-
fileprivate 限制实体只能在其定义的文件内部访问。如果功能的部分实现细节只需要在文件内使用时,可以使用 fileprivate 来将其隐藏。
-
private 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 private 来将其隐藏。
-
Open 只能作用于类和类的成员,它和Public 的区别如下: Public 或者其它更严访问级别的类,只能在其定义的模块内部被继承。 Public 或者其它更严访问级别的类成员,只能在其定义的模块内部的子类中重写。 Open 的类,可以在其定义的模块中被继承,也可以在引用它的模块中被继承。(就是说open能够实现其他模块中被继承,但是public不行)
-
12、闭包[weak self]
避免循环引用(内存管理相关知识点,什么是强引用弱引用?循环引用有什么问题?如何避免?)
-
简单来说:强引用就是每次实例化赋值,引用计数都会+1。强引用在计数为0的时候释放,但循环引用可能导致引用计数不能被减为0,一直不会释放。弱引用就是用来解决这个问题。
-
避免循环引用,可以有用弱引用或者无主引用。
13、Model使用struct,不用class原因(值类型、线程安全)
-
结构体是值类型,当结构体实例赋值给其他实例的时候是通过复制的方式,因此不需要担心改变原始实例来改变其状态值。
-
因为它们是复制的,所以不需要担心线程安全问题,也不需要额外的内存管理开销。
-
appdelegate和scenedelegate的区别:
-
在 iOS 应用生命周期管理中,AppDelegate 和 SceneDelegate 都扮演着重要的角色,但它们的职责和使用场景有所不同。这种区别主要是由于 iOS 应用架构的演进,特别是随着多任务和多场景(Multitasking and Multi-scene)支持的引入。
-
AppDelegate
-
AppDelegate 是传统的 iOS 应用代理,负责管理应用的生命周期事件。它是基于单任务(Single-task)模型的,主要处理应用的启动、进入后台、从后台恢复等事件。AppDelegate 中常见的回调方法包括:
-
application(_:didFinishLaunchingWithOptions:):应用启动时调用。
-
applicationWillResignActive(_:):应用即将变为非活动状态。
-
applicationDidEnterBackground(_:):应用进入后台。
-
applicationWillEnterForeground(_:):应用将进入前台。
-
applicationDidBecomeActive(_:):应用变为活动状态。
-
applicationWillTerminate(_:):应用即将终止。
-
-
SceneDelegate
-
SceneDelegate 是随着 iOS 13 和 iPadOS 的引入而新增的,用于支持多任务和多场景模型。每个场景(Scene)代表应用中的一个独立任务或用户交互的上下文,例如多个窗口或多个应用视图。
-
SceneDelegate 负责管理特定场景的生命周期,包括场景的创建、更新、暂停、恢复和销毁。这使得应用能够更灵活地处理多窗口和多任务操作。SceneDelegate 中常见的回调方法包括:
-
scene(_:willConnectTo:options:):场景即将连接到会话时调用。
-
sceneDidBecomeActive(_:):场景变为活动状态。
-
sceneWillResignActive(_:):场景即将变为非活动状态。
-
sceneWillEnterForeground(_:):场景将进入前台。
-
sceneDidEnterBackground(_:):场景进入后台。
-
scene(_:didDisconnect:):场景断开连接。
-
-
区别与联系
-
职责:AppDelegate 管理整个应用的生命周期,而 SceneDelegate 管理单个场景的生命周期。
-
架构:AppDelegate 基于单任务模型,SceneDelegate 支持多任务和多场景模型。
-
应用范围:在支持多场景的设备上,SceneDelegate 可以有多个实例,每个场景一个;而 AppDelegate 只有一个实例。
-
兼容性:在 iOS 13 及更高版本中,如果需要支持多场景,应使用 SceneDelegate;对于只支持单任务的应用,仍然可以使用 AppDelegate。
-
-
dateFormatter.dateFormat = "HH:mm:ss"
#之后每次直接使用,不需要每次都设置一下格式。
2、 self?
(弱引用表原理)
-
为什么使用弱引用
-
当你在一个函数或闭包中使用
self
时,通常创建了一个对当前实例的强引用。如果这个引用是闭包中唯一的引用,那么它将永远不会被释放,因为闭包一直引用着self
,而self
又引用着闭包,从而形成
-