SwiftUI的优缺点

2019年WWDC大会上,苹果在压轴环节向大众宣布了基于Swift语言构建的全新UI框架——SwiftUI,开发者可通过它快速为所有的Apple平台创建美观、动态的应用程序。推荐大量使用struct代替类。
SwiftUI 就是⼀种声明式的构建界面的用户接口工具包。
SwiftUI使用声明式的语法构建UI,我们只需要向系统声明UI的View样式,以及View如何转换状态,其他的过程都交给系统去处理。
SwiftUI的界面不再像UIKit那样,用ViewController 承载各种UIVew控件,而是一切皆View,所以可以把View切分成各种细致化的组件,然后通过组合的方式拼装成最终的界面,这种视图的拼装方式提高了界面开发的灵活性和复用性。因此,视图组件化是SwiftUI很大的亮点。
SwiftUI是声明式的构建方式,我们只需要声明好界面系统会自动转换状态,搭建界面更加的简单
声明式语法和指令式语法的区别:

声明式的我们需要提前声明好每个view的各种状态,以及状态转变的条件。后续界面和用户在互动时,系统会帮我们自动进行状态切换。
指令式的我们需要给每个view先设置好默认状态,后续界面和用户在互动时,需要通过指令不停的去转变view的状态
因此声明式的UI是提前声明好各种状态,系统会自动帮我们进行状态切换。指令式的UI是通过我们设定的指令来转换状态
比如界面调整、用户交互、机型适配,UIKit都需要手动调整view,对于SwiftUI我们只需要声明好我们想要的样式,系统会帮我们去调整view。
可以这么说,SwiftUI相比于UIKit更加的抽象化了
统一苹果终端
在 SwiftUI 出现之前,苹果不同的设备之前的开发框架并不互通,增加开发者所需消耗的时间精力,也不利于构建跨平台的软件体验
SwiftUI具有了跨平台性,苹果的平台都可以使用,iOS、macOS、tvOS、watchOS
降低界面开发难度
UIKit 的基本思想要求ViewController 承担绝⼤部分职责,它需要协调 model,view 以及⽤⼾交互。这带来了巨⼤的sideeffect 以及⼤量的状态
SwiftUI是声明式的构建方式,我们只需要声明好界面系统会自动转换状态,搭建界面更加的简单
更加高效
默认使用Metal渲染,性能非常高,比UIKit要好
更扁平化的内联数据结构去分配内存,值类型。占用内存很少(所以在轻应用的开发更适合使用SwiftUI)
代码量相比UIKit要更少,效率更高
更好的配合Swift语言
SwiftUI 使用了大量 Swift 的语言特性
声明式语法
与UIKit布局相比,更加的抽象化,只需要向系统声明界面样式以及样式变化条件,其他的系统会帮我们实现,不需要我们自己去调整视图
链式调用属性
链式调用是 Swift 语言的一种特性,就是使用函数式编程,可以像链条那样不断地调用函数,中间不需要断开。使用这种方式可以大大减少代码量。
除了系统提供的属性可以使用之外,开发者也可以进行自定义
例如将不同字体、字号、行间距、颜色等属性统合起来,可以组合成为一个叫「标题」的文字属性。之后凡是需要将某一行文字设置成标题,直接添加这个自定义的属性即可,使用这种方式进行开发无疑能够极大的避免无意义的重复工作,更快的搭建应用界面框架。
界面元素的组件化
UIKit耦合了很多的操作逻辑,很难进行移植,更遑论组件化了
而SwiftUI仅仅声明界面样式,所以是可以将复杂视图的拆分出来组件化
甚至还可以在其他平台使用,以此跨平台
一般我个人会将视图组件区分为基础组件、布局组件和功能组件
与UIKit互相兼容
把 UIKit 中已有的部分进行封装,提供给 SwiftUI 使用。开发者需要做的仅仅是遵循UIViewRepresentable协议即可
并且在已有的项目中,也可以仅用 SwiftUI 制作一部分的 UI 界面
两种代码的风格是截然不同的,但在使用上却基本没有性能的损失。在最终的运行效果上,用户也无法分辨出两种界面框架的不同。
真实数据源(Source of truth)(重点)
SwiftUI中的数据源一定会是真实的,也就是准确的
在OC中,一个view的状态由多种因素导致的,不同的来源,不同的逻辑操作(因此需要考虑及时更新界面)
因此在Swift中,提供了单一数据源的说法
只要在属性声明时加上 @State 等关键词,就可以将该属性和界面元素联系起来,在每次数据改动后,都有机会决定是否更新视图。
系统将所有的属性都集中到一起进行管理和计算,也不再需要手写刷新的逻辑。
因为在 SwiftUI 中,页面渲染前会将开发者描述的界面状态储存为结构体,更新界面就是将之前状态的结构体销毁,然后生成新的状态。
而在绘制界面的过程中,会自动比较视图中各个属性是否有变化,如果发生变化,便会更新对应的视图,避免全局绘制和资源浪费。
使用这种方式,读和写都集中在一处,开发者就能够更好地设计数据结构,比较方便的增减类型和排查问题。而不用再考虑线程、原子状态、寻找最新数据等各种细节,再决定通知相关的界面进行刷新。

UIKit 的基本思想要求ViewController 承担绝⼤部分职责,它需要协调 model,view 以及⽤⼾交互。这带来了巨⼤的sideeffect 以及⼤量的状态

SwiftUI不仅为Apple的平台带来了一种新的构建UI的方式,还有全新的Swift编码风格;
可以推断出:SwiftUI会出现很多组件库,方便前端开发;
支持热更新,这一点可能让更多的开发者拥抱SwiftUI;
虽然SwiftUI优点很多,但是其使用的门槛很高,只能在iOS 13以上的系统使用;仅这点,很多公司和开发者望而却步,目前主流应用最低支持iOS 9,至少3年之内,SwiftUI只能作为一个理论的知识储备,所以其还有很长的路要走;
说了它的优点,再说下它的缺点:
1.由于大量Struct来实现页面,甚至事件处理也写在Struct里面。要知道在Struct里是无法打印日志,这样造成无法建立强大的日志跟踪功能。一个好的软件一定要一个强大的日志跟踪系统。上次我开启了苹果的iOS内存日志功能,发现两个页面切换一次打印的日志就达到50兆–100兆的日志。可见苹果的内存管理好,是靠多少人的不断努力和打印跟踪内存分配和使用,只是对我们应用层开发者屏蔽了大量底层细节。自己的方便是建立在他人辛苦上的,没有无缘无故的方便。
2.由于大量Struct来实现页面,它没有deinit(类似oc的dealloc函数)。这样造成无法跟踪页面和view等的内存释放跟踪,定位是否有内存泄漏。无法使用采用hook技术的MLeaksFinder库检查是否内存泄漏。当然部分Struct可以用final class代替,但是和它提倡的采用Struc减少内存相悖。实际上采用SwiftUI大都采用Struct
3.由于大量Struct来实现页面,闪退时无法打印出页面的类名,日志跟踪时也显示不出页面的类名。点击UI View Hierarchy,打印的页面View是看不懂的内容,如:TtGC7SwiftUI14_UIHostingViewVS_7AnyView: 0x109d12880。不是正确的类名。并且页面层级稍微多两层,点击UI View Hierarchy就可能出不出来UI View Hierarchy了。这很不利于我们定位当前页面是那个类。
在这里插入图片描述
看到无法打印当前在那个页面的view,无法根据打印内存在工程中搜索到对应的view,不利于熟悉代码。

在这里插入图片描述
这个是开启僵尸进程后看到的闪退日志,可以看到无法看到那个类闪退了。
在这里插入图片描述
这个是oc的页面层级结构,可以看到各个组件层级很清晰,页面类名显示的很清晰,一个不熟悉代码的人很容易找到对应页面类,找到对应页面,很容易熟悉代码。
4.由于SwiftUI采用的是盒子的设计概念。手机加载时由于内存问题,通常用表格实现下拉内容多的页面,采用表格复用减少内存加载。而SwiftUI在用VStack实现下拉时可能遇到上下滑动时,页面元素乱飞的不可思议场景。不知道如何解决。

struct ChannelSingleView: View {@EnvironmentObject var server: ServerManager@EnvironmentObject var geoHallModel: GeoHallViewModel@EnvironmentObject var messageViewModel: MessagePageViewModelvar channel: ServerResponse.ChannelModelvar idx: Intvar angle: Double {if let id = channel.id {return geoHallModel.channelAngleDict[id] ?? 0.0}return 0.0}private var isSelected: Bool {return channel.id == geoHallModel.currChannel?.id}var body: some View {VStack(alignment: .center, spacing: 0) {channelAvatar.rotation3DEffect(.degrees(angle), axis: (x: 0, y: 1, z: 0.2))if let name = channel.name {Text(name).styleText(.size12, .black)}}.frame(width: 51 * .pointRatio).overlay {Color.bgcGray.opacity(isSelected ? 0 : 0.7)}.animation(.default, value: channel.top).id(channel.id).onTapGesture {if !isSelected {let canChangeTab = CustomTempVars.shared.canChangeTabif canChangeTab {CustomTempVars.shared.canChangeTab = falseTask {var channel = channelchannel.roomID = await server.getChannelRoom(channel.id ?? "")geoHallModel.switchChannel(channel)}withAnimation(.interpolatingSpring(stiffness: 20, damping: 5)) {if let id = channel.id {geoHallModel.channelAngleDict[id]! += 360}}}}geoHallModel.clearReadDot(channel)}.ignoresSafeArea().padding(.trailing, .size05).padding(.leading, .size05)
//        .padding(.trailing, 9 * .pointRatio).padding(.top, .safeAreaTop + .size05).padding(.bottom, .size05)
//        .padding(.leading, 9 * .pointRatio)}
}

在这里插入图片描述
这个是正常页面

在这里插入图片描述
这个是下滑后上滑动页面元素乱跳的一种场景

5.因为SwiftUI是一切皆view,大量使用Struct,当实现所有页面默认失败页面和无数据时,oc是采用ViewController基类快速实现,不知道SwiftUI实现。
6.当页面发送网络请求时,一般需要一个蒙层和动画,防止用户重复触发请求导致各种异常。oc是采用第三方控件在keywindow加入蒙层和动画的方式实现。SwiftUI可能有,我是不知道。这就是需要技术栈的积累问题。
7.当A页面进入B页面,B页面查询数据时,报错需要弹出toast弹窗,并退出B页面到A页面。一般toast弹窗是现实在keywindow上的,但是kewindow是B页面,而由于自动返回A页面,B页面销毁了,就看不到这个toast弹窗了。使用MBProgressHUD时就会遇到该问题。有的app为了解决这个问题人为延迟推出B页面,这样降低用户的体验流畅度,不完美。另一用采用SVProgressHUD来实现,它是异步时弹窗,显示调用弹窗和实际显示弹窗有一个时间差,正好在B页面调用弹窗,在回到A页面时,弹窗出来,所以能显示出来。我们遇到过一个问题,一个请求后台在时机毫秒内返回,蒙层弹窗还没有出来请求回来了,并且取消弹窗,实际上SVProgressHUD还没有出来,导致后面没有取消处理了,一直在哪里转圈了。最佳解决方案是创建一个优先级高于当前级别的window显示蒙层和动画。不知道SwiftUI处理这些细节问题。
8.SwiftUI提倡view组件化,可能产生view处理过多,不利于专类做专门的事情的原则。要平衡view的复杂性和复用性,不推荐过度的复用。
一个完美的产品不是完成基本功就完事,要完美处理各种细节。SwiftUI在技术栈积累不够和各种组件前不建议实际应用。作为知识储备是必要,立即实施风险太大,没有必要。我们很欣赏SwiftUI简洁性和快速开发,很多细节需要我们解决。方便和功能强大是相悖的。正如java和C/C++的关系。使用java基本不关注指针使用和内存使用,方便好学。C/C++需要处理烦人的指针和内存使用问题,正因为它更接近底层,所以能实现java实现不了的问题和响应比java快30%。一个好的软件不取决于采用那种语言,而是取决于开发软件的人。SwiftUI还任重道远,不要过于追求新技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/15799.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

会话机制【Cookie 和 Session】,登陆页面的模拟实现

前言 小亭子正在努力的学习编程,接下来将开启JavaEE的学习~~ 分享的文章都是学习的笔记和感悟,如有不妥之处希望大佬们批评指正~~ 同时如果本文对你有帮助的话,烦请点赞关注支持一波, 感激不尽~~ 目录 前言 Cookie 和 Session 是什么 Cookie…

Android Binder进程间通讯原理分析

Binder IPC原理 Android系统是基于Linux内核开发的。Linux开发提供了丰富的进程间通讯机制,例如管道、信号、消息队列、共享内存、插口(Socket) 。而Binder是一套新的通讯工具。 Binder通信采用了c/s架构,所以我们包含了 Client&…

LwIP系列(5):TCP 3次握手+4次挥手+状态机转换

前言 TCP的3次握手、4次挥手以及TCP状态机,是TCP的核心概念,我们在分析LwIp中TCP相关代码流程,也需要熟悉这些流程,本文就详细介绍这些概念。 TCP 3次握手、应用数据交互、4次挥手完整流程 TCP 为什么是3次握手,而不…

【设计模式】第十九章:访问者模式详解及应用案例

系列文章 【设计模式】七大设计原则 【设计模式】第一章:单例模式 【设计模式】第二章:工厂模式 【设计模式】第三章:建造者模式 【设计模式】第四章:原型模式 【设计模式】第五章:适配器模式 【设计模式】第六章&…

删除有序链表中的重复元素II——牛客24

题目描述 法一)直接删除法 class Solution{ public:ListNode* deleteDuplicates(ListNode* head) {if(headNULL) return NULL;ListNode* dummy new ListNode(0);dummy->next head;ListNode* cur dummy;while(cur->next!NULL && cur->next->n…

raid5两块磁盘掉线导致阵列崩溃的服务器数据恢复案例

服务器数据恢复环境: DELL PowerVault系列某型号存储,15块硬盘搭建了一组RAID5磁盘阵列。 服务器故障&检测: 存储设备raid5阵列中一块磁盘由于未知原因离线,管理员对该磁盘阵列进行了同步操作。在同步的过程中又有一块磁盘指示…

【算法与数据结构】232、LeetCode用栈实现队列

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:这道题要求我们用栈模拟队列(工作上一定没人这么搞)。程序当中,pus…

岛屿数量 (力扣) dfs + bfs JAVA

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的…

Spring Boot原理分析(一):项目启动(上)——@SpringBootApplication

文章目录 〇、准备工作一、SpringBootApplication.java源码解析1.源码2.自定义注解3.组合注解4.注解ComponentScan过滤器 5.注解SpringBootConfigurationConfiguration 6.注解EnableAutoConfiguration 本文章是Spring Boot源码解读与原理分析系列博客的第一篇,将会介…

阐述kubernetes部署:基础设施安装

基础设施部署 持久卷的建立 请参考:《持久卷的建立》 elasticsearch部署 一、设置远程扩展字典 不使用自定义字典请忽略此步骤 首先更改ES中IK插件的配置: vi/opt/kubernetes/es/IKAnalyzer.cfg.xml 按您的实际设置的秘钥配置secret_value: …

springboot+echarts +mysql制作数据可视化大屏(四图)

作者水平低,如有错误,恳请指正!谢谢!!!!! 项目简单,适合大学生参考 分类专栏还有其它的可视化博客哦! 专栏地址:https://blog.csdn.net/qq_559…

操作系统12:I/O系统的功能、模型、接口及 I/O 设备和设备控制器

目录 1、I/O系统的功能、模型和接口 (1)I/O系统的基本功能 1.1 - 隐藏物理设备的细节 1.2 - 与设备的无关性 1.3 - 提高处理机和I/O设备的利用率 1.4 - 对 I/O 设备进行控制 1.5 - 确保对设备的正确共享 1.6 - 错误处理 (2&#xff…