該了解的iOS(二)之核心
iOS的核心篇章我主要說深入理解設(shè)計(jì)模式、組件化,模塊化,插件化和app運(yùn)行時(shí)的質(zhì)量監(jiān)控等知識(shí)點(diǎn),不是說別的篇章不是核心篇章,如優(yōu)化,靜、動(dòng)態(tài)分析等,我會(huì)有專門的篇章來詳解它們,不能使一篇文章讀下來找不到重點(diǎn)。
深入理解設(shè)計(jì)模式
設(shè)計(jì)模式中有六大基本原則,它們是設(shè)計(jì)模式的核心指導(dǎo)思想,分別為:
- 單一職責(zé)原則(single responsibility principle):實(shí)現(xiàn)類要職責(zé)單一
- 里氏替換原則(liskov substitution principle):不要破壞繼承體系
- 依賴倒置原則(dependence inversion principle):面向接口編程
- 接口隔離原則(interface segregation principle):在設(shè)計(jì)接口的時(shí)候要精簡(jiǎn)單一
- 迪米特法則(law of demeter):降低耦合
- 開放封閉原則(open close principle):對(duì)外擴(kuò)展開放,修改關(guān)閉
六大原則的遵循,并不是是和否的選擇,更不要刻意去追求,“物極必反,過猶不及”,靈活使用。
設(shè)計(jì)模式又分為創(chuàng)建型(工廠,單例),結(jié)構(gòu)型(代理,組合),行為型(策略,觀察者)等三個(gè)方面.
- 單例模式:一般會(huì)封裝一個(gè)靜態(tài)屬性,并提供靜態(tài)實(shí)例的創(chuàng)建方法
static singleton *__singleton = nil;
+ (singleton *)sharedSingleton {
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
__singleton = [[singleton alloc]init];
});
return __singleton;
}
其中static singleton *__singleton = nil;為靜態(tài)變量,
類方法為(singleton *)sharedSingleton ,
dispatch_once_t 的函數(shù)是GCD提供的,在整個(gè)生命周期只執(zhí)行一次
- 委托模式:委托是為了降低一個(gè)對(duì)象的復(fù)雜度和耦合度,使其能夠具有通用性而將其一些處理置于委托對(duì)象中的編碼方法,它通過delegate屬性保持委托對(duì)象,使它來實(shí)現(xiàn)協(xié)議。那委托對(duì)象如何建立引用關(guān)系呢?對(duì)象.delegate = self。如一些復(fù)雜的控件(UITableView),除了實(shí)現(xiàn)委托協(xié)議,還需要實(shí)現(xiàn)數(shù)據(jù)協(xié)議,委托對(duì)象主要對(duì)控件對(duì)象的事件和狀態(tài)作出響應(yīng),而數(shù)據(jù)源對(duì)象是為控件提供數(shù)據(jù)。
- 觀察者模式:觀察者模式中兩個(gè)具體應(yīng)用就是(通知,KVO),通知機(jī)制是“一對(duì)多”的對(duì)象間通信,不僅有本地通知還有遠(yuǎn)程通知。KVO是在對(duì)象屬性發(fā)生變化時(shí)通知會(huì)被直接發(fā)送給觀察者
- MVC模式:這個(gè)已經(jīng)眾所周知的了,其實(shí)它是一種復(fù)合設(shè)計(jì)模式,由(觀察者+策略+組合)模式等組成,具體就是不同而異,可以用成MVP,MVVM,都是根據(jù)你項(xiàng)目來合理使用了。
分層架構(gòu)設(shè)計(jì)
衡量一個(gè)軟件架構(gòu)的好壞是它的可復(fù)用性和可擴(kuò)展性來滿足用戶不斷變化的需求,我認(rèn)為采用分層架構(gòu)的設(shè)計(jì),實(shí)現(xiàn)低耦合,高內(nèi)聚。
- 表示層:用戶與系統(tǒng)交互的組件集合
- 業(yè)務(wù)邏輯層:系統(tǒng)的核心業(yè)務(wù)處理層
- 數(shù)據(jù)持久層:數(shù)據(jù)持久層用戶訪問信息系統(tǒng),對(duì)文件進(jìn)行讀寫操作
- 信息系統(tǒng)層:系統(tǒng)的數(shù)據(jù)來源,可以是數(shù)據(jù)庫(kù),文件或者網(wǎng)絡(luò)數(shù)據(jù)等
組件化、模塊化、插件化
- 組件:強(qiáng)調(diào)復(fù)用,避免重復(fù)造輪子。在開發(fā)過程中,一些核心技術(shù)或者常用框架,出于安全性和穩(wěn)定性的考慮,不想被外界知道,所以會(huì)把核心代碼打包成庫(kù),只暴露出頭文件以供使用。
- 模塊:強(qiáng)調(diào)職責(zé),這是一個(gè)可實(shí)現(xiàn)的單元,其核心是內(nèi)聚和分離,如登錄注冊(cè)可分離成獨(dú)立的模塊
- 插件化:強(qiáng)調(diào)化大為小,插件化在運(yùn)行時(shí)合并模塊。
組件模塊化思想是軟件開發(fā)的基本思想,組件化的目的就是復(fù)用代碼模塊,解除業(yè)務(wù)和代碼的耦合,組件間彼此分離,便于開發(fā),測(cè)試,維護(hù)和獨(dú)立編譯。解決主工程打包時(shí)組件間重復(fù)引用的問題,引入bridge組件庫(kù)或pod添加,避免組件間直接耦合,具體請(qǐng)看:http://www.itdecent.cn/p/210825875f39
質(zhì)量監(jiān)控
有時(shí)會(huì)說“沒有崩潰,就沒有我”,在快速迭代的過程中可能因?yàn)橹饔^原因,如技術(shù)有限,知識(shí)盲點(diǎn),粗心導(dǎo)致的crash,或者客觀原因版本升級(jí),手機(jī)卡頓,網(wǎng)絡(luò)卡頓等導(dǎo)致的crash。雖然我們不能阻止崩潰,但我們可以去發(fā)現(xiàn)它,分析它,解決它。一套完美的質(zhì)量監(jiān)控應(yīng)該包括:基本驗(yàn)證,穩(wěn)定性,兼容性,安全性,功能測(cè)試和線上質(zhì)量檢測(cè)
- 基本驗(yàn)證:版本號(hào),簽名,賬號(hào),代碼質(zhì)量等
- 穩(wěn)定性:一些性能指標(biāo),如CPU,內(nèi)存,流暢度,包大小,啟動(dòng)時(shí)間,響應(yīng)速度等
- 兼容性:多版本適配,多網(wǎng)絡(luò),低電量等
- 安全性:加密,解密。惡意攻擊等
- 功能測(cè)試:?jiǎn)卧獪y(cè)試,自動(dòng)化測(cè)試
- 線上質(zhì)量檢測(cè):用戶反饋,crash收集,數(shù)據(jù)統(tǒng)計(jì)等
有一些驗(yàn)證是需要人工的,但是有一些可以通過自動(dòng)化的,去嘗試寫一些腳本保證質(zhì)量過關(guān),當(dāng)然也可以選擇一些好用的第三方平臺(tái)。
- 關(guān)于線下性能監(jiān)控:蘋果公司官方就有一個(gè)性能監(jiān)控工具 Instruments。它是一款被集成在 Xcode 里,專門用來在線下進(jìn)行性能分析的工具.Instruments 的功能非常強(qiáng)大,比如說 Energy Log 就是用來監(jiān)控耗電量的,Leaks 就是專門用來監(jiān)控內(nèi)存泄露問題的,Network 就是用來專門檢查網(wǎng)絡(luò)情況的,Time Profiler 就是通過時(shí)間采樣來分析頁(yè)面卡頓問題的。
- 對(duì)于線上性能監(jiān)控:監(jiān)控代碼不要侵入到業(yè)務(wù)代碼中,采用性能消耗最小的監(jiān)控方案,主要集中在 CPU 使用率、FPS 的幀率和內(nèi)存這三個(gè)方面
CPU
<!--cpu_usage 就是 CPU 使用率-->
+ (integer_t)cpuUsage {
thread_act_array_t threads; //int 組成的數(shù)組比如 thread[1] = 5635
mach_msg_type_number_t threadCount = 0; //mach_msg_type_number_t 是 int 類型
const task_t thisTask = mach_task_self();
//根據(jù)當(dāng)前 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
<!--對(duì) FPS 的監(jiān)控也可以比較簡(jiǎn)單的實(shí)現(xiàn):通過注冊(cè) CADisplayLink 得到屏幕的同步刷新率,記錄每次刷新時(shí)間,然后就可以得到 FPS-->
- (void)start {
self.dLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsCount:)];
[self.dLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
// 方法執(zhí)行幀率和屏幕刷新率保持一致
- (void)fpsCount:(CADisplayLink *)displayLink {
if (lastTimeStamp == 0) {
lastTimeStamp = self.dLink.timestamp;
} else {
total++;
// 開始渲染時(shí)間與上次渲染時(shí)間差值
NSTimeInterval useTime = self.dLink.timestamp - lastTimeStamp;
if (useTime < 1) return;
lastTimeStamp = self.dLink.timestamp;
// fps 計(jì)算
fps = total / useTime;
total = 0;
}
}
內(nèi)存
<!--蘋果公司介紹說 phys_footprint 才是實(shí)際使用的物理內(nèi)存。-->
struct task_vm_info {
mach_vm_size_t virtual_size; // 虛擬內(nèi)存大小
integer_t region_count; // 內(nèi)存區(qū)域的數(shù)量
integer_t page_size;
mach_vm_size_t resident_size; // 駐留內(nèi)存大小
mach_vm_size_t resident_size_peak; // 駐留內(nèi)存峰值
...
/* added for rev1 */
mach_vm_size_t phys_footprint; // 物理內(nèi)存
...
<!--取出 phys_footprint 字段的值,就能夠監(jiān)控到實(shí)際物理內(nèi)存的使用情況了-->
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;
}
東西不多,但很實(shí)用。接下來我會(huì)詳談項(xiàng)目中的網(wǎng)絡(luò)請(qǐng)求,性能優(yōu)化和一些高級(jí)知識(shí)點(diǎn)的總結(jié)