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