CODE REVIEW

项目代码CR:

1、dateFormatter优化,避免多次创建(性能问题)

  • 在MyweatherViewController中创建新的属性,不用每次创建一个dateFormatter

var dateFormatter = DateFormatter() 
#在第一次使用的时候 

2、 self? (弱引用表原理)

  • 为什么使用弱引用

    • 当你在一个函数或闭包中使用 self 时,通常创建了一个对当前实例的强引用。如果这个引用是闭包中唯一的引用,那么它将永远不会被释放,因为闭包一直引用着 self,而 self 又引用着闭包,从而形成循环强引用。这会导致内存泄漏。

  • self?为什么修改它?

    • 弱引用只指向sideTable,不再指向原来的对象。一旦强引用计数为0时,可以彻底释放原来对象的空间。如果每次去访问sideTable取值麻烦.

    • 使用 self? 意味着你期望 self 可能是 nil。如果 selfnil,那么使用 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 类型,这通常通过 JSONSerializationJSONEncoder 来实现。

 

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、属性privatepublic区分(代码规范)

  • 通过修饰符 openpublicinternalfileprivateprivate 来声明实体的访问级别

    • openpublic 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 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 又引用着闭包,从而形成

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值