该了解的iOS(二)之核心
iOS的核心篇章我主要说深入理解设计模式、组件化,模块化,插件化和app运行时的质量监控等知识点,不是说别的篇章不是核心篇章,如优化,静、动态分析等,我会有专门的篇章来详解它们,不能使一篇文章读下来找不到重点。
深入理解设计模式
设计模式中有六大基本原则,它们是设计模式的核心指导思想,分别为:
- 单一职责原则(single responsibility principle):实现类要职责单一
- 里氏替换原则(liskov substitution principle):不要破坏继承体系
- 依赖倒置原则(dependence inversion principle):面向接口编程
- 接口隔离原则(interface segregation principle):在设计接口的时候要精简单一
- 迪米特法则(law of demeter):降低耦合
- 开放封闭原则(open close principle):对外扩展开放,修改关闭
六大原则的遵循,并不是是和否的选择,更不要刻意去追求,“物极必反,过犹不及”,灵活使用。
设计模式又分为创建型(工厂,单例),结构型(代理,组合),行为型(策略,观察者)等三个方面.
- 单例模式:一般会封装一个静态属性,并提供静态实例的创建方法
static singleton *__singleton = nil;
+ (singleton *)sharedSingleton {
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
__singleton = [[singleton alloc]init];
});
return __singleton;
}
其中static singleton *__singleton = nil;为静态变量,
类方法为(singleton *)sharedSingleton ,
dispatch_once_t 的函数是GCD提供的,在整个生命周期只执行一次
- 委托模式:委托是为了降低一个对象的复杂度和耦合度,使其能够具有通用性而将其一些处理置于委托对象中的编码方法,它通过delegate属性保持委托对象,使它来实现协议。那委托对象如何建立引用关系呢?对象.delegate = self。如一些复杂的控件(UITableView),除了实现委托协议,还需要实现数据协议,委托对象主要对控件对象的事件和状态作出响应,而数据源对象是为控件提供数据。
- 观察者模式:观察者模式中两个具体应用就是(通知,KVO),通知机制是“一对多”的对象间通信,不仅有本地通知还有远程通知。KVO是在对象属性发生变化时通知会被直接发送给观察者
- MVC模式:这个已经众所周知的了,其实它是一种复合设计模式,由(观察者+策略+组合)模式等组成,具体就是不同而异,可以用成MVP,MVVM,都是根据你项目来合理使用了。
分层架构设计
衡量一个软件架构的好坏是它的可复用性和可扩展性来满足用户不断变化的需求,我认为采用分层架构的设计,实现低耦合,高内聚。
- 表示层:用户与系统交互的组件集合
- 业务逻辑层:系统的核心业务处理层
- 数据持久层:数据持久层用户访问信息系统,对文件进行读写操作
- 信息系统层:系统的数据来源,可以是数据库,文件或者网络数据等
组件化、模块化、插件化
- 组件:强调复用,避免重复造轮子。在开发过程中,一些核心技术或者常用框架,出于安全性和稳定性的考虑,不想被外界知道,所以会把核心代码打包成库,只暴露出头文件以供使用。
- 模块:强调职责,这是一个可实现的单元,其核心是内聚和分离,如登录注册可分离成独立的模块
- 插件化:强调化大为小,插件化在运行时合并模块。
组件模块化思想是软件开发的基本思想,组件化的目的就是复用代码模块,解除业务和代码的耦合,组件间彼此分离,便于开发,测试,维护和独立编译。解决主工程打包时组件间重复引用的问题,引入bridge组件库或pod添加,避免组件间直接耦合,具体请看:https://www.jianshu.com/p/210825875f39
质量监控
有时会说“没有崩溃,就没有我”,在快速迭代的过程中可能因为主观原因,如技术有限,知识盲点,粗心导致的crash,或者客观原因版本升级,手机卡顿,网络卡顿等导致的crash。虽然我们不能阻止崩溃,但我们可以去发现它,分析它,解决它。一套完美的质量监控应该包括:基本验证,稳定性,兼容性,安全性,功能测试和线上质量检测
- 基本验证:版本号,签名,账号,代码质量等
- 稳定性:一些性能指标,如CPU,内存,流畅度,包大小,启动时间,响应速度等
- 兼容性:多版本适配,多网络,低电量等
- 安全性:加密,解密。恶意攻击等
- 功能测试:单元测试,自动化测试
- 线上质量检测:用户反馈,crash收集,数据统计等
有一些验证是需要人工的,但是有一些可以通过自动化的,去尝试写一些脚本保证质量过关,当然也可以选择一些好用的第三方平台。
- 关于线下性能监控:苹果公司官方就有一个性能监控工具 Instruments。它是一款被集成在 Xcode 里,专门用来在线下进行性能分析的工具.Instruments 的功能非常强大,比如说 Energy Log 就是用来监控耗电量的,Leaks 就是专门用来监控内存泄露问题的,Network 就是用来专门检查网络情况的,Time Profiler 就是通过时间采样来分析页面卡顿问题的。
- 对于线上性能监控:监控代码不要侵入到业务代码中,采用性能消耗最小的监控方案,主要集中在 CPU 使用率、FPS 的帧率和内存这三个方面
CPU
<!--cpu_usage 就是 CPU 使用率-->
+ (integer_t)cpuUsage {
thread_act_array_t threads; //int 组成的数组比如 thread[1] = 5635
mach_msg_type_number_t threadCount = 0; //mach_msg_type_number_t 是 int 类型
const task_t thisTask = mach_task_self();
//根据当前 task 获取所有线程
kern_return_t kr = task_threads(thisTask, &threads, &threadCount);
if (kr != KERN_SUCCESS) {
return 0;
}
integer_t cpuUsage = 0;
// 遍历所有线程
for (int i = 0; i < threadCount; i++) {
thread_info_data_t threadInfo;
thread_basic_info_t threadBaseInfo;
mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
if (thread_info((thread_act_t)threads[i], THREAD_BASIC_INFO, (thread_info_t)threadInfo, &threadInfoCount) == KERN_SUCCESS) {
// 获取 CPU 使用率
threadBaseInfo = (thread_basic_info_t)threadInfo;
if (!(threadBaseInfo->flags & TH_FLAGS_IDLE)) {
cpuUsage += threadBaseInfo->cpu_usage;
}
}
}
assert(vm_deallocate(mach_task_self(), (vm_address_t)threads, threadCount * sizeof(thread_t)) == KERN_SUCCESS);
return cpuUsage;
}
FPS
<!--对 FPS 的监控也可以比较简单的实现:通过注册 CADisplayLink 得到屏幕的同步刷新率,记录每次刷新时间,然后就可以得到 FPS-->
- (void)start {
self.dLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsCount:)];
[self.dLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
// 方法执行帧率和屏幕刷新率保持一致
- (void)fpsCount:(CADisplayLink *)displayLink {
if (lastTimeStamp == 0) {
lastTimeStamp = self.dLink.timestamp;
} else {
total++;
// 开始渲染时间与上次渲染时间差值
NSTimeInterval useTime = self.dLink.timestamp - lastTimeStamp;
if (useTime < 1) return;
lastTimeStamp = self.dLink.timestamp;
// fps 计算
fps = total / useTime;
total = 0;
}
}
内存
<!--苹果公司介绍说 phys_footprint 才是实际使用的物理内存。-->
struct task_vm_info {
mach_vm_size_t virtual_size; // 虚拟内存大小
integer_t region_count; // 内存区域的数量
integer_t page_size;
mach_vm_size_t resident_size; // 驻留内存大小
mach_vm_size_t resident_size_peak; // 驻留内存峰值
...
/* added for rev1 */
mach_vm_size_t phys_footprint; // 物理内存
...
<!--取出 phys_footprint 字段的值,就能够监控到实际物理内存的使用情况了-->
uint64_t memoryUsage() {
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (result != KERN_SUCCESS)
return 0;
return vmInfo.phys_footprint;
}
东西不多,但很实用。接下来我会详谈项目中的网络请求,性能优化和一些高级知识点的总结
网友评论