iOS面试准备 - ios篇
iOS面試準(zhǔn)備 - ios篇
ios面試準(zhǔn)備 - objective-c篇
ios面試準(zhǔn)備 - 網(wǎng)絡(luò)篇
IOS面試準(zhǔn)備 - C++篇
iOS面試準(zhǔn)備 - 其他篇
運(yùn)行時(shí)
https://juejin.cn/post/6844903586216804359
Runtime消息發(fā)送機(jī)制
首先進(jìn)行方法的查找:
1)iOS調(diào)用一個(gè)方法時(shí),實(shí)際上會(huì)調(diào)用objc_msgSend(receiver, selector, arg1, arg2, …),該方法第一個(gè)參數(shù)是消息接收者,第二個(gè)參數(shù)是方法名,剩下的參數(shù)是方法參數(shù);
2)iOS調(diào)用一個(gè)方法時(shí),會(huì)先去該類的方法緩存列表里面查找是否有該方法,如果有直接調(diào)用,否則走第3)步;
3)去該類的方法列表里面找,找到直接調(diào)用,把方法加入緩存列表;否則走第4)步;
4)沿著該類的繼承鏈繼續(xù)查找,找到直接調(diào)用,把方法加入緩存列表;否則消息轉(zhuǎn)發(fā)流程;
如果沒(méi)有找到方法,開(kāi)始消息的轉(zhuǎn)發(fā)流程:
1)動(dòng)態(tài)消息解析。檢查是否重寫了resolveInstanceMethod 方法,如果返回YES則可以通過(guò)class_addMethod 動(dòng)態(tài)添加方法來(lái)處理消息,否則走第2)步;
2)消息target轉(zhuǎn)發(fā)。forwardingTargetForSelector 用于指定哪個(gè)對(duì)象來(lái)響應(yīng)消息。如果返回nil 則走第3)步;
3)消息轉(zhuǎn)發(fā)。這步調(diào)用 methodSignatureForSelector 進(jìn)行方法簽名,這可以將函數(shù)的參數(shù)類型和返回值封裝。如果返回 nil 執(zhí)行第四步;否則返回 methodSignature,則進(jìn)入 forwardInvocation ,在這里可以修改實(shí)現(xiàn)方法,修改響應(yīng)對(duì)象等,如果方法調(diào)用成功,則結(jié)束。否則執(zhí)行第4)步;
4)報(bào)錯(cuò) unrecognized selector sent to instance。
load和initialize
+load在main函數(shù)之前被Runtime調(diào)用,+initialize 方法是在類或它的子類收到第一條消息之前被調(diào)用的,這里所指的消息包括實(shí)例方法和類方法的調(diào)用。
load
當(dāng)類被引用進(jìn)項(xiàng)目的時(shí)候就會(huì)執(zhí)行l(wèi)oad函數(shù)(在main函數(shù)開(kāi)始執(zhí)行之前),與這個(gè)類是否被用到無(wú)關(guān),每個(gè)類的load函數(shù)只會(huì)自動(dòng)調(diào)用一次.由于load函數(shù)是系統(tǒng)自動(dòng)加載的,因此不需要調(diào)用父類的load函數(shù),否則父類的load函數(shù)會(huì)多次執(zhí)行。
當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定(其執(zhí)行順序與類別在Compile Sources中出現(xiàn)的順序一致)
當(dāng)然當(dāng)有多個(gè)不同的類的時(shí)候,每個(gè)類load 執(zhí)行順序與其在Compile Sources出現(xiàn)的順序一致
initialize
initialize在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。即使類文件被引用進(jìn)項(xiàng)目,但是沒(méi)有使用,initialize不會(huì)被調(diào)用。由于是系統(tǒng)自動(dòng)調(diào)用,也不需要再調(diào)用 [super initialize] ,否則父類的initialize會(huì)被多次執(zhí)行。假如這個(gè)類放到代碼中,而這段代碼并沒(méi)有被執(zhí)行,這個(gè)函數(shù)是不會(huì)被執(zhí)行的。
1.父類的initialize方法會(huì)比子類先執(zhí)行
2.當(dāng)子類未實(shí)現(xiàn)initialize方法時(shí),會(huì)調(diào)用父類initialize方法,子類實(shí)現(xiàn)initialize方法時(shí),會(huì)覆蓋父類initialize方法.
3.當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類中的方法,只執(zhí)行一個(gè)(會(huì)執(zhí)行Compile Sources 列表中最后一個(gè)Category 的initialize方法)
怎么確保在load和initialize的調(diào)用只執(zhí)行一次:
由于load和initialize可能會(huì)調(diào)用多次,所以在這兩個(gè)方法里面做的初始化操作需要保證只初始化一次,用dispatch_once來(lái)控制
runtime應(yīng)用
關(guān)聯(lián)對(duì)象(Objective-C Associated Objects)給分類增加屬性
方法魔法(Method Swizzling)方法添加和替換和KVO實(shí)現(xiàn)
消息轉(zhuǎn)發(fā)(熱更新)解決Bug(JSPatch)
實(shí)現(xiàn)NSCoding的自動(dòng)歸檔和自動(dòng)解檔
實(shí)現(xiàn)字典和模型的轉(zhuǎn)換(YYModel)
RunLooper
https://segmentfault.com/a/1190000023390697
線程和 RunLoop 之間是一一對(duì)應(yīng)的,其關(guān)系是保存在一個(gè)全局的 Dictionary 里。線程剛創(chuàng)建時(shí)并沒(méi)有 RunLoop,如果你不主動(dòng)獲取,那它一直都不會(huì)有。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí),RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)。
系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:
kCFRunLoopDefaultMode: App的默認(rèn) Mode,通常主線程是在這個(gè) Mode 下運(yùn)行的。
UITrackingRunLoopMode: 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響。
UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用。
GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到。
kCFRunLoopCommonModes: 這是一個(gè)占位的 Mode,沒(méi)有實(shí)際作用。
與GCD關(guān)系:當(dāng)調(diào)用 dispatch_async(dispatch_get_main_queue(), block) 時(shí),libDispatch 會(huì)向主線程的 RunLoop 發(fā)送消息,RunLoop會(huì)被喚醒,并從消息中取得這個(gè) block,并在回調(diào)
解決NSTimer事件在列表滾動(dòng)時(shí)不執(zhí)行問(wèn)題
因?yàn)槎〞r(shí)器默認(rèn)是運(yùn)行在NSDefaultRunLoopMode,在列表滾動(dòng)時(shí)候,主線程會(huì)切換到UITrackingRunLoopMode,導(dǎo)致定時(shí)器回調(diào)得不到執(zhí)行。
有兩種解決方案:
● 指定NSTimer運(yùn)行于 NSRunLoopCommonModes下。
● 在子線程創(chuàng)建和處理Timer事件,然后在主線程更新 UI。
GCD
GCD是核心XNU內(nèi)核級(jí)實(shí)現(xiàn)的高效多線程編程功能。
dispatch_queue_create 創(chuàng)建隊(duì)列
dispatch_get_main_queue() 獲取主隊(duì)列
dispatch_get_global_queue();并行隊(duì)列,優(yōu)先級(jí)依次為:ios7:高,默認(rèn),低,后臺(tái). ios7之后:用戶交互,用戶需要,默認(rèn),工具級(jí),后臺(tái),沒(méi)有指定。
dispatch_set_target_queue 變更優(yōu)先級(jí)
dispatch_after 在一定時(shí)間之后執(zhí)行
dispatch_time(DISPATCH_TIME_NOW, 200ull * NSEC_PER_SEC); 時(shí)間變量
dispatch_group_t group = dispatch_group_create(); 創(chuàng)建組
dispatch_group_async(group, queue, ^{NSLog(@“1”);}); 使用組
dispatch_group_notify(group, queue, ^{NSLog(@“4”);}); 在一組最后執(zhí)行
dispatch_barrier_async 在這個(gè)調(diào)用之前的async先完成,再調(diào)用這個(gè)的代碼塊,在這個(gè)之調(diào)用后,在他之后的async才開(kāi)始執(zhí)行
dispatch_sync是等待處理執(zhí)行結(jié)束后,再繼續(xù)
dispatch_apply 自動(dòng)適配線程,循環(huán)處理指定次數(shù)
dispatch_once 保證只執(zhí)行一次,可以用在單例模式
+(instancetype)sharedInstance{static MyClass *sharedInstance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{sharedInstance = [[MyClass alloc]init];});return _sharedInstance; }dispatch_semaphore_create 創(chuàng)建信號(hào)量 自旋鎖
dispatch_semaphore_signal 信號(hào)量加1
dispatch_semaphore_wait 信號(hào)量減1,如果信號(hào)量等于0就阻塞等待
NSOperation
NSOperation是基于GCD更高一層的封裝。
一般使用 NSInvocationOperation 或者 NSBlockOperation ,也可以自己繼承實(shí)現(xiàn),需要實(shí)現(xiàn)main函數(shù)。
NSOperationQueue 是執(zhí)行隊(duì)列
添加到主隊(duì)列的都會(huì)在主隊(duì)列中執(zhí)行
添加到其他隊(duì)列的都會(huì)在子線程中執(zhí)行
隊(duì)列最大并發(fā)數(shù)設(shè)置 maxConcurrentOperationCount
函數(shù):
NSOperationQueue的函數(shù):
addOperation:把NSOperation 加到隊(duì)列中
addExecutionBlock: 直接把代碼塊作為任務(wù)加到隊(duì)列中
cancelAllOperations; NSOperationQueue提供的方法,可以取消隊(duì)列的所有操作,所有任務(wù)取消,包括正在執(zhí)行的,還未執(zhí)行的。
setSuspended:(BOOL)b; 可設(shè)置任務(wù)的暫停和恢復(fù),YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
NSOperation的函數(shù):
cancel; ,可取消單個(gè)操作
.qualityOfService = NSQualityOfServiceBackground;設(shè)置優(yōu)先級(jí)有:非常低,低,通常,高,非常高
[A addDependency:B] A在B執(zhí)行完了過(guò)后開(kāi)始執(zhí)行
鎖
https://www.jianshu.com/p/5445411fb53c
NSLock
互斥鎖,底層用pthread_mutex實(shí)現(xiàn)
lock、unlock
trylock:能加鎖返回 YES 并執(zhí)行加鎖操作,相當(dāng)于 lock,反之返回 NO
lockBeforeDate:這個(gè)方法表示會(huì)在傳入的時(shí)間內(nèi)嘗試加鎖,若能加鎖則執(zhí)行加鎖**操作并返回 YES,反之返回 NO
@synchronized
由recursive_mutex(互斥遞歸鎖)實(shí)現(xiàn),最終還是用了pthread_mutex。最大的問(wèn)題就是,效率低,傳入對(duì)象必須等待之前的鎖執(zhí)行完成之后才能執(zhí)行,無(wú)法達(dá)到異步的效果。
NSCondition
條件變量也是互斥鎖,底層是的pthread_cond條件變量和pthread_mutex互斥量的封裝
wait:進(jìn)入等待狀態(tài)
waitUntilDate::讓一個(gè)線程等待一定的時(shí)間
signal:喚醒一個(gè)等待的線程
broadcast:喚醒所有等待的線程
dispatch_semaphore 自旋鎖
dispatch_semaphore_create(1): 傳入值必須 >=0, 若傳入為 0 則阻塞線程并等待timeout,時(shí)間到后會(huì)執(zhí)行其后的語(yǔ)句
dispatch_semaphore_wait(signal, overTime):可以理解為 lock,會(huì)使得 signal 值 -1
dispatch_semaphore_signal(signal):可以理解為 unlock,會(huì)使得 signal 值 +1
NSRecursiveLock
是對(duì)pthread_mutex的封裝
遞歸鎖可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖。這主要是用在循環(huán)或遞歸操作中。
atomic
自旋鎖
OSSpinLock 自旋鎖
互斥鎖和信號(hào)量的區(qū)別
互斥量用于線程的互斥,信號(hào)線用于線程的同步。
互斥量值只能為0/1,信號(hào)量值可以為非負(fù)整數(shù)。也就是說(shuō),一個(gè)互斥量只能用于一個(gè)資源的互斥訪問(wèn),它不能實(shí)現(xiàn)多個(gè)資源的多線程互斥問(wèn)題。信號(hào)量可以實(shí)現(xiàn)多個(gè)同類資源的多線程互斥和同步。
線程和進(jìn)程
線程和進(jìn)程的區(qū)別
進(jìn)程是資源分配的最小單位,線程是CPU調(diào)度的最小單位
一個(gè)進(jìn)程可以包含多個(gè)線程
不同進(jìn)程間數(shù)據(jù)很難共享
同一進(jìn)程下不同線程間數(shù)據(jù)很易共享
進(jìn)程要比線程消耗更多的計(jì)算機(jī)資源
進(jìn)程間不會(huì)相互影響,一個(gè)線程掛掉將導(dǎo)致整個(gè)進(jìn)程掛掉
進(jìn)程可以拓展到多機(jī),進(jìn)程最多適合多核
進(jìn)程使用的內(nèi)存地址可以上鎖,即一個(gè)線程使用某些共享內(nèi)存時(shí),其他線程必須等它結(jié)束,才能使用這一塊內(nèi)存。
ios進(jìn)程間通信
URL scheme
Keychain 本質(zhì)是數(shù)據(jù)庫(kù),經(jīng)過(guò)加密,為不同app存儲(chǔ)敏感信息
UIPasteBoard 剪切板
socket
APP Groups 同組的不同app,通過(guò)相同組名讀取文件
AirDrop 分享(設(shè)備間信息傳遞)
UIActivityViewController 封裝AirDrop
線程間通信
線程間通信方法包含進(jìn)程間通信方法(如上),因?yàn)閮蓚€(gè)進(jìn)程間通信,其實(shí)就是兩個(gè)線程間在通信。單個(gè)進(jìn)程內(nèi)的線程通信有如下方法。
共享內(nèi)存
共享磁盤文件
NSObject對(duì)象 performSelectorOnMainThread 或者 performSelector: onThread:
GCD
NSOperationQueue
應(yīng)用啟動(dòng)流程
解析Info.plist:加載相關(guān)信息, 簽名驗(yàn)證,
沙箱和進(jìn)程建立
加載動(dòng)態(tài)庫(kù)
dyld(動(dòng)態(tài)鏈接器)會(huì)首先讀取mach-o文件的Header和load commands。接著就知道了這個(gè)可執(zhí)行文件依賴的動(dòng)態(tài)庫(kù),然后根據(jù)遞歸的依賴遞歸加載所有動(dòng)態(tài)庫(kù),同時(shí)緩存到 dyld shared cache 提高加載效率。
Rebase 修正內(nèi)部(指向當(dāng)前mach-o文件)的指針指向(因?yàn)榈刂肥请S機(jī)的,蘋果會(huì)運(yùn)用地址空間布局隨機(jī)化技術(shù)ASLR來(lái)給指針的起始地址一個(gè)隨機(jī)的偏移量,)
Bind 修正外部指針指向
objC的runtime初始化(ObjC setup):ObjC相關(guān)Class的注冊(cè)、category注冊(cè)等。
類的load方法調(diào)用
main函數(shù) UIApplicationMain
app生命周didFinishLaunchWithOptions,applicationDidBecomeActive
vc生命周期
編譯流程
先編譯cocopods里面的所有依賴文件
編譯信息寫入輔助信息,創(chuàng)建編譯后的文件架構(gòu):將項(xiàng)目的文件結(jié)構(gòu)對(duì)應(yīng)表、將要執(zhí)行的腳本、項(xiàng)目依賴庫(kù)的文件結(jié)構(gòu)對(duì)應(yīng)表寫成文件
運(yùn)行預(yù)設(shè)的腳本。如 Cocoapods 會(huì)在 Build Phases 中預(yù)設(shè)一些腳本
編譯 .m 文件,生成可執(zhí)行文件 Mach-O。每次進(jìn)行了 LLVM 的完整流程:前端(詞法分析 - 語(yǔ)法分析 - 生成 IR)、優(yōu)化器(優(yōu)化 IR)、后端(生成匯編 - 鏈接 - 生成可執(zhí)行文件)
鏈接需要的.framework庫(kù)
拷貝資源文件到目標(biāo)包
編譯 xib 文件
鏈接 xib 文件
編譯 Asset 文件。
處理 info.plist
執(zhí)行 CocoaPods 腳本,將在編譯項(xiàng)目前已編譯好的依賴庫(kù)和相關(guān)資源拷貝到包中。
拷貝 Swift 標(biāo)準(zhǔn)庫(kù)
創(chuàng)建 .app 文件并對(duì)其簽名
離屏渲染
iOS 離屏渲染探究
手動(dòng)觸發(fā)離屏渲染
可以通過(guò)設(shè)置layer的shouldRasterize為YES來(lái)觸發(fā)離屏渲染,在某些場(chǎng)景下,打開(kāi) shouldRasterize 可以將一個(gè)layer反復(fù)利用,從而達(dá)到提升效率的優(yōu)勢(shì)。
事件傳遞
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {// 1.判斷當(dāng)前控件能否接收事件if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;// 2. 判斷點(diǎn)在不在當(dāng)前控件if ([self pointInside:point withEvent:event] == NO) return nil;// 3.從后往前遍歷自己的子控件NSInteger count = self.subviews.count;for (NSInteger i = count - 1; i >= 0; i--) {UIView *childView = self.subviews[i];// 把當(dāng)前控件上的坐標(biāo)系轉(zhuǎn)換成子控件上的坐標(biāo)系CGPoint childP = [self convertPoint:point toView:childView];UIView *fitView = [childView hitTest:childP withEvent:event];if (fitView) { // 尋找到最合適的viewreturn fitView;}}// 循環(huán)結(jié)束,表示沒(méi)有比自己更合適的viewreturn self; }推送通知
本地通知
本地通知是在程序中指定某個(gè)時(shí)間,或者在多少時(shí)間倒計(jì)時(shí),或者在特定條件之后,出現(xiàn)在設(shè)備的狀態(tài)欄消息中的功能。
本地通知使用:在AppDelegate.m中application didFinishLauch方法中對(duì)通知方法執(zhí)行注冊(cè);在AppDelegate的didRegisterUserNotificationSettings回調(diào)里知道注冊(cè)是否成功;在 didReceiveLocalNotification中響應(yīng)通知點(diǎn)擊事件;發(fā)本地通知用UILocalNotification ,可配置標(biāo)題,內(nèi)容,聲音,程序圖標(biāo)通知數(shù)目,觸發(fā)事件等信息。
移除全部本地通知:
取所有通知:
[UIApplication sharedApplication].scheduledLocalNotifications;根據(jù)移除指定通知:
[[UIApplication sharedApplication] cancelLocalNotification:notification]遠(yuǎn)程通知
每臺(tái)設(shè)備只要聯(lián)網(wǎng)就會(huì)和蘋果的 APNs服務(wù)器建立一個(gè) 長(zhǎng)連接,這樣只要通過(guò)蘋果的APNs服務(wù)器,我們自己的服務(wù)器就可以間接的和設(shè)備保持連接了.
通知注冊(cè)跟本地通知一樣。
如果用戶同意,蘋果會(huì)根據(jù)應(yīng)用的 bundleID 和 手機(jī)UDID 生成 deviceToken,然后調(diào)用 -application: didRegisterForRemoteNotificationsWithDeviceToken : 方法返回 devicetoken。
相應(yīng)遠(yuǎn)程推送函數(shù)是 didReceiveRemoteNotification
應(yīng)用程序5個(gè)狀態(tài)
停止運(yùn)行-應(yīng)用程序已經(jīng)終止,或者還未啟動(dòng)。
不活動(dòng)-應(yīng)用程序處于前臺(tái)但不再接收事件(例如,用戶在app處于活動(dòng)時(shí)鎖住了設(shè)備)。
活動(dòng)-app處于“使用中”的狀態(tài)。
后臺(tái)-app不再屏幕上顯示,但它仍然執(zhí)行代碼。
掛起-app仍然駐留內(nèi)存但不再執(zhí)行代碼。
按下Home鍵時(shí),app從活動(dòng)狀態(tài)轉(zhuǎn)入后臺(tái),絕大部分app通常在幾秒內(nèi)就從后臺(tái)變成了掛起。
在內(nèi)存吃緊的時(shí)候,iphone會(huì)首先關(guān)閉那些掛起的app
后臺(tái)運(yùn)行方案
1、Background Audio,這是后臺(tái)的音頻,這個(gè)很早之前便有,也是iOS設(shè)備中用得最多的后臺(tái)應(yīng)用,調(diào)用這個(gè)接口可以實(shí)現(xiàn)后臺(tái)的音樂(lè)播放。
2、Location Services,這是后臺(tái)的定位,系統(tǒng)會(huì)擁有統(tǒng)一頁(yè)面進(jìn)行管理。
3、VoIP,后臺(tái)語(yǔ)音服務(wù),類似Skype通話應(yīng)用需要調(diào)用,可進(jìn)行后臺(tái)的語(yǔ)音通話。
4、Newsstand,報(bào)刊雜志后臺(tái)自動(dòng)下載更新,其能夠自動(dòng)實(shí)時(shí)更新。
5、Background Task Completion,這個(gè)接口早在iOS 4時(shí)候便擁有,其可以供任意類型的APP使用,不過(guò)在舊系統(tǒng)中,這個(gè)接口的后臺(tái)限制運(yùn)行時(shí)間僅為10分鐘,意味著當(dāng)應(yīng)用退至后臺(tái),其后臺(tái)運(yùn)行僅能持續(xù)10分鐘便會(huì)轉(zhuǎn)至休眠狀態(tài)。iOS 7中對(duì)這個(gè)接口作出了改變,原來(lái)的為連續(xù)10分鐘,即不論你這10分鐘內(nèi)用戶是否關(guān)閉屏幕進(jìn)入休眠狀態(tài),應(yīng)用仍然會(huì)在后臺(tái)等待10分鐘完結(jié)后推出,而新的改進(jìn)為假如遇到關(guān)閉屏幕休眠的情況,這后臺(tái)運(yùn)行的10分鐘便會(huì)跟隨一同休眠,剩余的后臺(tái)時(shí)間將會(huì)留待用戶再一次喚醒設(shè)備才計(jì)算。這樣后臺(tái)運(yùn)行的時(shí)間仍然為10分鐘,但并不連續(xù),這樣做的優(yōu)點(diǎn)為省電。
如現(xiàn)在有一些詞典應(yīng)用帶有后臺(tái)復(fù)制選詞功能,實(shí)際上其是利用了這個(gè)接口,如果用戶開(kāi)啟詞典后并推出,即使屏幕關(guān)閉,但詞典仍然在后臺(tái)運(yùn)行,電量消耗還是比較大的,在iOS 7上,這個(gè)問(wèn)題可以得到解決。
6、 Remote Notification,這是本次較大的一個(gè)改進(jìn)接口,以往聊天類應(yīng)用接受推送后點(diǎn)進(jìn)去需要再收一次信息,這情況在QQ、微信等應(yīng)用上最為明顯。不過(guò)擁有了這個(gè)接口后,這情況將不復(fù)存在,以后推送將能夠直接啟動(dòng)后臺(tái)任務(wù)。值得注意的是remote notification支持silent notification(靜默推送),這樣dropbox這類同步應(yīng)用可以在后臺(tái)以最節(jié)能的模式實(shí)時(shí)靜默同步了,類似布卡漫畫這種也可以推送正在追的漫畫的新章節(jié)并在后臺(tái)靜默下載,待到下載好再給用戶發(fā)送一個(gè)本地推送,用戶點(diǎn)開(kāi)即看無(wú)需再聯(lián)網(wǎng)。
7、Background Transfer Service,后臺(tái)上傳下載。iOS最接近傳統(tǒng)多任務(wù)的后臺(tái)接口,可供任意類型的app調(diào)用,無(wú)時(shí)間限制。應(yīng)用場(chǎng)景包括后臺(tái)上傳和下載數(shù)據(jù),這使得游戲后臺(tái)更新數(shù)據(jù)包,后臺(tái)上傳視頻等等都成為可能,但是正如其名字,它只能用于處理上傳下載這種傳輸類的任務(wù),類似后臺(tái)剪切板監(jiān)控這種它就無(wú)能為力了。
自動(dòng)布局框架Masonry
https://segmentfault.com/a/1190000019569119
Masonry是一個(gè)對(duì)系統(tǒng)NSLayoutConstraint進(jìn)行封裝的第三方自動(dòng)布局框架,采用鏈?zhǔn)骄幊痰姆绞教峁┙o開(kāi)發(fā)者API。
CoreData
https://juejin.cn/post/6844903805369188366
1.簡(jiǎn)述
CoreData是數(shù)據(jù)存儲(chǔ)框架,采用了一種對(duì)象關(guān)系映射的存儲(chǔ)關(guān)系。
CoreData一個(gè)比較大的優(yōu)勢(shì)在于在使用CoreData過(guò)程中不需要我們編寫SQL語(yǔ)句,也就是將OC對(duì)象存儲(chǔ)于數(shù)據(jù)庫(kù),也可以將數(shù)據(jù)庫(kù)數(shù)據(jù)轉(zhuǎn)為OC對(duì)象(數(shù)據(jù)庫(kù)數(shù)據(jù)與OC對(duì)象相互轉(zhuǎn)換)。
2.CoreData幾個(gè)類
(1)NSManagedObjectContext
托管對(duì)象上下文,數(shù)據(jù)庫(kù)的大多數(shù)操作是在這個(gè)類操作
(2)NSManagedObjectModel
托管對(duì)象模型,其中一個(gè)托管對(duì)象模型關(guān)聯(lián)到一個(gè)模型文件,里面存儲(chǔ)著數(shù)據(jù)庫(kù)的數(shù)據(jù)結(jié)構(gòu)。
(3)NSPersistentStoreCoordinator
持久化存儲(chǔ)協(xié)調(diào)器,主要負(fù)責(zé)協(xié)調(diào)上下文玉存儲(chǔ)的區(qū)域的關(guān)系。
(4)NSManagedObject
托管對(duì)象類,其中CoreData里面的托管對(duì)象都會(huì)繼承此類。
FMDB
內(nèi)部封裝c語(yǔ)言 通過(guò) sqlite3系列函數(shù)操作數(shù)據(jù)庫(kù)。
有三個(gè)主要的類:
1.FMDatabase – 表示一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫(kù)。 用來(lái)執(zhí)行SQLite的命令。
2.FMResultSet – 表示FMDatabase執(zhí)行查詢后結(jié)果集
3.FMDatabaseQueue – 如果你想在多線程中執(zhí)行多個(gè)查詢或更新,你應(yīng)該使用該類。這是線程安全的。
創(chuàng)建FMDatabase對(duì)象時(shí)參數(shù)為SQLite數(shù)據(jù)庫(kù)文件路徑。該路徑可以是以下三種之一:
1…文件路徑。該文件路徑無(wú)需真實(shí)存,如果不存在會(huì)自動(dòng)創(chuàng)建。
2…空字符串(@”")。表示會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù),當(dāng)FMDatabase 鏈接關(guān)閉時(shí),文件也被刪除。
3.NULL. 將創(chuàng)建一個(gè)內(nèi)在數(shù)據(jù)庫(kù)。同樣的,當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)會(huì)被銷毀。
打開(kāi)數(shù)據(jù)庫(kù)
在和數(shù)據(jù)庫(kù)交互 之前,數(shù)據(jù)庫(kù)必須是打開(kāi)的。如果資源或權(quán)限不足無(wú)法打開(kāi)或創(chuàng)建數(shù)據(jù)庫(kù),都會(huì)導(dǎo)致打開(kāi)失敗
常用命令
SELECT、CREATE, UPDATE, INSERT,ALTER,COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE (等)
只要不是以SELECT開(kāi)頭的命令都是UPDATE命令。執(zhí)行更新返回一個(gè)BOOL值。YES表示執(zhí)行成功,否則表示有那些錯(cuò)誤 。你可以調(diào)用 -lastErrorMessage 和 -lastErrorCode方法來(lái)得到更多信息。
執(zhí)行查詢
SELECT命令就是查詢,執(zhí)行查詢的方法是以 -excuteQuery開(kāi)頭的。執(zhí)行查詢時(shí),如果成功返回FMResultSet對(duì)象, 錯(cuò)誤返回nil. 與執(zhí)行更新相當(dāng),支持使用 NSError**參數(shù)。同時(shí),你也可以使用 -lastErrorCode和-lastErrorMessage獲知錯(cuò)誤信息。
添加表字段的sql語(yǔ)句怎么寫 1、判斷表是否打開(kāi) 2、判斷表中是否存在當(dāng)前的一個(gè)或者多個(gè)字段 3、如果不存在添加字段
性能優(yōu)化
應(yīng)用啟動(dòng)優(yōu)化:合并動(dòng)態(tài)庫(kù);刪除無(wú)用類;刪除無(wú)用靜態(tài)變量;將不必須在+load方法中做的事情延遲到+initialize中;盡量使用純代碼編寫,減少xib的使用;耗時(shí)操作異步執(zhí)行;不用AutoLayout;優(yōu)化代碼降低包大小
懶加載和復(fù)用對(duì)象
是否透明屬性opaque YES表示不透明。
列表性能提升: reuseIdentifier避免每次渲染cell都重建;盡量非透明opaque 為YES;緩存行高;耗時(shí)如網(wǎng)絡(luò)請(qǐng)求,異步加載緩存結(jié)果;Shadow Path替代直接用陰影;減少層次結(jié)構(gòu)。
不要阻塞主線程,耗時(shí)操作異步到子線程
正確選擇Collection:Arrays: 有序的一組值。使用index來(lái)lookup很快,使用value lookup很慢, 插入/刪除很慢;Dictionaries: 存儲(chǔ)鍵值對(duì)。 用鍵來(lái)查找比較快;Sets: 無(wú)序的一組值。用值來(lái)查找很快,插入/刪除很快。
使用緩存,比如NSCache,緩存文件等
重用大開(kāi)銷對(duì)象,恰當(dāng)使用單例模式
webview重用
數(shù)據(jù)存儲(chǔ)
使用NSUserDefaults 存沙盒文件,小文件
plist 文件儲(chǔ)存(xcode配置里面就有這個(gè)文件)
歸檔保存,將字典數(shù)組或者實(shí)現(xiàn)了(NSCoding)協(xié)議的對(duì)象轉(zhuǎn)換成字節(jié)流NSData,再寫入文件。使用NSKeyedArchiver和NSKeyedUnarchiver
SQLite數(shù)據(jù)庫(kù) (開(kāi)源庫(kù) FMDB、Realm等)
使用 Core Data
autoreleasepool
自動(dòng)釋放池塊提供了一種機(jī)制,您可以通過(guò)該機(jī)制放棄對(duì)象的所有權(quán),但避免立即釋放它的可能性(例如當(dāng)您從方法返回對(duì)象時(shí))
Cocoa 總是希望代碼在自動(dòng)釋放池塊中執(zhí)行,否則自動(dòng)釋放的對(duì)象不會(huì)被釋放并且您的應(yīng)用程序會(huì)泄漏內(nèi)存。
使用自己的自動(dòng)釋放池塊:
1.如果您正在編寫不基于 UI 框架的程序,例如命令行工具。
2.如果您編寫一個(gè)創(chuàng)建許多臨時(shí)對(duì)象的循環(huán)。
您可以在循環(huán)內(nèi)使用自動(dòng)釋放池塊在下一次迭代之前處理這些對(duì)象。在循環(huán)中使用自動(dòng)釋放池塊有助于減少應(yīng)用程序的最大內(nèi)存占用。
3.如果你產(chǎn)生一個(gè)輔助線程。
您必須在線程開(kāi)始執(zhí)行后立即創(chuàng)建自己的自動(dòng)釋放池塊;否則,您的應(yīng)用程序?qū)⑿孤?duì)象。(Cocoa 應(yīng)用程序中的每個(gè)線程都維護(hù)自己的自動(dòng)釋放池塊堆棧。如果您正在編寫僅 Foundation 程序或分離線程,則需要?jiǎng)?chuàng)建自己的自動(dòng)釋放池塊。)
main函數(shù)的autoreleasepool :autorelease對(duì)象的釋放時(shí)機(jī)是由RunLoop控制的,會(huì)在當(dāng)前RunLoop每次循環(huán)結(jié)束時(shí)釋放。
iOS 自動(dòng)釋放池ARC與MRC
內(nèi)存分布
內(nèi)存泄漏
內(nèi)存泄漏的原因
在用C/C++時(shí),創(chuàng)建對(duì)象后未銷毀,比如調(diào)用malloc后不free、調(diào)用new后不delete;
調(diào)用CoreFoundation里面的C方法后創(chuàng)建對(duì)對(duì)象后不釋放。比如調(diào)用CGImageCreate不調(diào)用CGImageRelease;
循環(huán)引用。
NSTimer
有些注冊(cè)未解開(kāi)注冊(cè)
子線程未關(guān)閉runlooper或者有死循環(huán),死鎖等讓該關(guān)閉的線程關(guān)閉不了
檢測(cè)循環(huán)引用
靜態(tài)代碼分析。 通過(guò)Xcode->Product->Anaylze分析結(jié)果來(lái)處理;
動(dòng)態(tài)分析。用MLeaksFinder(只能檢測(cè)OC泄露)或者Instrument或者OOMDetector(能檢測(cè)OC與C++泄露)。
其他
對(duì)于視圖或者圖層來(lái)說(shuō),frame是一個(gè)虛擬屬性,是根據(jù)bounds,position和transform計(jì)算而來(lái),所以當(dāng)其中任何一個(gè)值發(fā)生改變,frame都會(huì)變化。相反,改變frame的值同樣會(huì)影響到他們當(dāng)中的值。
https://zsisme.gitbooks.io/ios-/content/chapter3/layout.html
為什么圓角和透明觸發(fā)離屏渲染:
當(dāng)App需要進(jìn)行額外的渲染和合并時(shí),例如按鈕設(shè)置圓角,我們是需要對(duì)UIButton這個(gè)控件中的所有圖層都進(jìn)行圓角+裁剪,然后再將合并后的結(jié)果存入幀緩存區(qū),再?gòu)膸彺嬷腥〕鼋挥善聊伙@示,這時(shí),在正常的渲染流程中,我們是無(wú)法做到對(duì)所有圖層進(jìn)行圓角裁剪的,因?yàn)樗怯靡粋€(gè)丟一個(gè)。所以我們需要提前將處理好的結(jié)果放入離屏緩沖區(qū),最后將幾個(gè)圖層進(jìn)行疊加合并,存放到站緩沖區(qū),最后屏幕上就是我們想實(shí)現(xiàn)的效果。
super class是怎么調(diào)用的
super 不同于 self,它不是個(gè)對(duì)象,而是個(gè) flag
用于 objc_msgSendSuper 的結(jié)構(gòu)體 objc_super 是編譯時(shí)確定的,里面包含了當(dāng)前類的父類信息
[super someMethod] 會(huì)去利用結(jié)構(gòu)體中的父類信息,從這個(gè)父類開(kāi)始順著繼承鏈向上查找,直到找到第一個(gè)實(shí)現(xiàn) - someMethod 這個(gè)方法的類
找到方法后,利用結(jié)構(gòu)體中的 receiver,也就是一開(kāi)始觸發(fā)這個(gè)方法調(diào)用的實(shí)例,調(diào)用這個(gè)方法實(shí)現(xiàn)。
所以說(shuō),[super class] 經(jīng)過(guò)這么一大圈的轉(zhuǎn)換,實(shí)際上變成了 [self class] 了。
weak的實(shí)現(xiàn)
Runtime維護(hù)了一個(gè)weak表,用于存儲(chǔ)指向某個(gè)對(duì)象的所有weak指針。
1、初始化時(shí):runtime初始化一個(gè)新的weak指針指向?qū)ο蟮牡刂贰?br /> 2、添加引用時(shí):更新指針指向,創(chuàng)建對(duì)應(yīng)的弱引用表。
3、釋放時(shí),首先根據(jù)對(duì)象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個(gè)entry從weak表中刪除。
靜態(tài)鏈接
靜態(tài)鏈接:每一個(gè).c的代碼源文件可以被理解成一個(gè)模塊,每一個(gè)模塊獨(dú)立編譯,再把所有編譯完的文件鏈接起來(lái),這個(gè)過(guò)程就是我們所說(shuō)的靜態(tài)鏈接。 靜態(tài)庫(kù): 例如.a和.framework。靜態(tài)庫(kù)鏈接時(shí),會(huì)被完整地復(fù)制到可執(zhí)行文件中,被使用到了多次,就會(huì)復(fù)制多份,這樣就有多份拷貝很冗余,鏈接時(shí)間長(zhǎng)了,還浪費(fèi)了內(nèi)存空間。
靜態(tài)鏈接流程:
1.空間與地址分配
每個(gè)單獨(dú)的文件編譯后都會(huì)生成一個(gè)符號(hào)表,靜態(tài)鏈接后這些表會(huì)被合并成一個(gè)全局符號(hào)表。合并的規(guī)則是相似段合并、數(shù)據(jù)段與數(shù)據(jù)段合并、代碼段與代碼段合并。
合并后每一個(gè)符號(hào)的的地址被確定,并寫入全局符號(hào)表中。
2.重定位符號(hào)
經(jīng)過(guò)空間與地址分配之后代碼段中指令用到的符號(hào)地址還沒(méi)有更新,想要確定符號(hào)的地址需要用到重定位表。編譯后.o文件中需要重定位的符號(hào)的相關(guān)信息會(huì)存入重定位表中。
動(dòng)態(tài)鏈接
程序編譯時(shí)并不會(huì)鏈接到目標(biāo)程序中,目標(biāo)程序只會(huì)存儲(chǔ)指向動(dòng)態(tài)庫(kù)的引用,在程序運(yùn)行時(shí)才被載入。:
共享內(nèi)存,節(jié)省資源,同一份庫(kù)可以被多個(gè)程序使用;減少打包之后的 app 的大小;
CoreGraphics,和CoreAnimation
為什么必須在主線程操作UI
UIKit并不是一個(gè)線程安全的類,UI操作涉及到渲染訪問(wèn)各種View對(duì)象的屬性,如果異步操作下會(huì)存在讀寫問(wèn)題,
而為其加鎖則會(huì)耗費(fèi)大量資源并拖慢運(yùn)行速度。
另一方面因?yàn)檎麄€(gè)程序的起點(diǎn)UIApplication是在主線程進(jìn)行初始化,所有的用戶事件都是在主線程上進(jìn)行傳遞(如點(diǎn)擊、拖動(dòng)),所以view只能在主線程上才能對(duì)事件進(jìn)行響應(yīng)。
而在渲染方面由于圖像的渲染需要以60幀的刷新率在屏幕上 同時(shí) 更新,在非主線程異步化的情況下無(wú)法確定這個(gè)處理過(guò)程能夠?qū)崿F(xiàn)同步更新。
省電
測(cè)試工具:xcode自帶工具:Energy Impact。可以圖形直觀展示CPU,GPU,定位,網(wǎng)絡(luò),后臺(tái),前臺(tái)等耗電占比。
省電的方案:
識(shí)別:想清楚你需要app在特定時(shí)刻需要完成哪些工作,如果是不必要的工作,考慮延后執(zhí)行或者省去。
優(yōu)化:優(yōu)化app的功能實(shí)現(xiàn),盡可能以更有效率的方式去完成功能。
合并:不需要立刻獲取,可以延后合并執(zhí)行,比如合并網(wǎng)絡(luò)
減少:在滿足需求的基礎(chǔ)上,盡量減少做重復(fù)工作的頻率。
耗電大戶:
網(wǎng)絡(luò):應(yīng)減少數(shù)據(jù)傳輸,合并網(wǎng)絡(luò)請(qǐng)求,適當(dāng)?shù)木W(wǎng)絡(luò)延時(shí)等
定位:精度越高,定位時(shí)間越長(zhǎng),越耗電
CPU:
GPU:UI不可見(jiàn)時(shí),應(yīng)該避免更新其內(nèi)容
傳感器和藍(lán)牙: 傳感器數(shù)據(jù)應(yīng)按需獲取,用完即停;藍(lán)牙應(yīng)該盡量分批、減少數(shù)據(jù)輪詢等操作
幀率FPS
代碼中檢測(cè):CADisplayLink
Xcode檢測(cè)工具:Core Animation
主線程卡頓監(jiān)測(cè): 通過(guò)開(kāi)辟一個(gè)子線程來(lái)監(jiān)測(cè)主線程的RunLoop,當(dāng)兩個(gè)狀態(tài)區(qū)域的耗時(shí)大于設(shè)定的閾值時(shí),即為一次卡頓。
判斷是否實(shí)現(xiàn)協(xié)議
conformsToProtocol
判斷有沒(méi)有實(shí)現(xiàn)dalegate的某個(gè)方法
respondsToSelector
通知
postNotification是同步方法。當(dāng)調(diào)用addObserver方法監(jiān)聽(tīng)通知,然后調(diào)用postNotification拋通知,postNotification會(huì)在當(dāng)前線程遍歷所有的觀察者,然后依次調(diào)用觀察者的監(jiān)聽(tīng)方法,調(diào)用完成后才會(huì)去執(zhí)行postNotification后面的代碼。
實(shí)現(xiàn)異步的通知使用:addObserverForName:object:queue:usingBlock來(lái)實(shí)現(xiàn)異步通知。
監(jiān)測(cè)野指針
xcode Run配置打開(kāi) 內(nèi)存涂鴉(Malloc Scribble),將釋放的內(nèi)存進(jìn)行涂鴉成固定值,導(dǎo)致使用野指針的時(shí)候一定crash
xcode Run配置打開(kāi) 僵尸對(duì)象,僵尸對(duì)象替換對(duì)象的dealloc方法,如果調(diào)用已經(jīng)dealloc過(guò)后的對(duì)象拋出異常
iOS常見(jiàn)崩潰以及總結(jié)
常見(jiàn)崩潰
非法參數(shù)
數(shù)組越界
KVO 重復(fù)一處觀察者,沒(méi)有實(shí)現(xiàn)observeValueForKeyPath:
kvc
對(duì)象接收到未知的消息
signal 信號(hào)量崩潰
捕獲crash
捕獲oc崩潰:NSSetUncaughtExceptionHandler 。如下
注冊(cè)SIGABRT, SIGBUS, SIGSEGV等信號(hào)發(fā)生時(shí)的處理函數(shù),處理Signal層面的crash。
例如如下
crash保護(hù)
hook相關(guān)的方法,用trycatch保護(hù),增加保護(hù)機(jī)制。
調(diào)用方法前,判斷 respondsToSelector
崩潰分析方法
用xcode查看崩潰
xcode->Window->Organizer->Crashes
在Archive的時(shí)候會(huì)生成.xcarchive文件,然后顯示包內(nèi)容就能夠在里面找到.dsYM文件。用友盟.dsYM分析,選中UUID,輸入崩潰地址。
** 路由方案 **
url 路由
target-action方案:
給組件封裝一層target對(duì)象來(lái)對(duì)外提供服務(wù),然后調(diào)用者通過(guò)依賴中間件來(lái)使用服務(wù);其中,中間件是通過(guò)runtime來(lái)調(diào)用組件的服務(wù),是真正意義上的解耦,也是該方案最核心的地方。不會(huì)對(duì)原來(lái)組件造成入侵;然后,通過(guò)實(shí)現(xiàn)中間件的category來(lái)提供服務(wù)給調(diào)用者,這樣使用者只需要依賴中間件,而組件則不需要依賴中間件。每個(gè)category對(duì)應(yīng)一個(gè)Target,Categroy中的方法對(duì)應(yīng)Action場(chǎng)景
protocol-class
就是通過(guò)protocol定義服務(wù)接口,組件通過(guò)實(shí)現(xiàn)該接口來(lái)提供接口定義的服務(wù),具體實(shí)現(xiàn)就是把protocol和class做一個(gè)映射,同時(shí)在內(nèi)存中保存一張映射表,使用的時(shí)候,就通過(guò)protocol找到對(duì)應(yīng)的class來(lái)獲取需要的服務(wù)。
ios的導(dǎo)航設(shè)計(jì)模式
平鋪導(dǎo)航( UITabbarController )
標(biāo)簽導(dǎo)航( UINavigationController )
樹形導(dǎo)航(UIPageViewController)
屬性修飾符
可以用strong和retain修飾同一個(gè)屬性
但是不能用strong和copy修飾同一屬性,運(yùn)行會(huì)報(bào)錯(cuò)
viewDidUnload在這里插入代碼片
是ios6后舍棄的 vc銷毀時(shí)回調(diào)的生命周期
WKWebview ios和js通信
ios調(diào)用js,在ios中:
js調(diào)用ios
在ios中注冊(cè)回調(diào)
在js中調(diào)用:
window.webkit.messageHandlers.hello.postMessage();真機(jī)調(diào)試前的準(zhǔn)備
創(chuàng)建登錄開(kāi)發(fā)者賬號(hào)
在本地用鑰匙串創(chuàng)建csr文件
進(jìn)入證書管理頁(yè)面,創(chuàng)建證書,上傳csr文件,生成cer證書,下載這個(gè)證書
雙擊下載證書,安裝到電腦的鑰匙串
創(chuàng)建appid
添加允許調(diào)試的設(shè)備的UDID
創(chuàng)建PP文件:選擇真機(jī)調(diào)試配置文件,填入appid,真機(jī)調(diào)試證書,最后生成并下載mobileprovision文件
雙擊pp文件,安裝到xcode上
xcode中Bundle identifier設(shè)置成appid,配置選擇pp文件,配置cer證書
總結(jié)
以上是生活随笔為你收集整理的iOS面试准备 - ios篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 磁力链接转换为种子文件 magnet t
- 下一篇: 二代测序技术之illumina测序技术原