1、dSYM你是如何分析的
方法1 使用XCode
這種方法可能是最容易的方法了。
要使用Xcode符號化 crash log,你需要下面所列的3個文件:
crash報告(.crash文件)
符號文件 (.dsymb文件)
應用程序文件 (appName.app文件,把IPA文件后綴改為zip,然后解壓,Payload目錄下的appName.app文件), 這里的appName是你的應用程序的名稱。
把這3個文件放到同一個目錄下,打開Xcode的Window菜單下的organizer,然后點擊Devices tab,然后選中左邊的Device Logs。
然后把.crash文件拖到Device Logs或者選擇下面的import導入.crash文件。
這樣你就可以看到crash的詳細log了。
方法2 使用命令行工具symbolicatecrash
有時候Xcode不能夠很好的符號化crash文件。我們這里介紹如何通過symbolicatecrash來手動符號化crash log。
在處理之前,請依然將“.app“, “.dSYM”和 ".crash"文件放到同一個目錄下。現在打開終端(Terminal)然后輸入如下的命令:
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
然后輸入命令:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash appName.crash appName.app > appName.log
現在,符號化的crash log就保存在appName.log中了。
方法3 使用命令行工具atos
如果你有多個“.ipa”文件,多個".dSYMB"文件,你并不太確定到底“dSYMB”文件對應哪個".ipa"文件,那么,這個方法就非常適合你。
特別當你的應用發布到多個渠道的時候,你需要對不同渠道的crash文件,寫一個自動化的分析腳本的時候,這個方法就極其有用。
具體方法 請百度
本文分析了拿到用戶的.crash文件之后,如何符合化crash文件的3種方法,分別有其適用場景,方法3適用于自動化crash文件的分析。
2.多線程有哪幾種?你更傾向于哪一種?
NSThread Cocoa NSOperation (使用NSOperation和NSOperationQueue) GCD (Grand Central Dispatch)
1.NSThread:(兩種創建方式)
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];
[myThread start];
優點:NSThread 比其他兩個輕量級。 缺點:需要自己管理線程的生命周期,線程同步,線程同步時對數據的加鎖會有一定的系統開銷。
2.Cocoa Operation
NSOperationQueue*oprationQueue= [[NSOperationQueuealloc] init];
oprationQueueaddOperationWithBlock:^{
//這個block語句塊在子線程中執行
}
優點:不需要關心線程管理,數據同步的事情。 Cocoa Operation 相關的類是 NSOperation ,NSOperationQueue。NSOperation是個抽象類,使用它必須用它的子類,可以實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。創建NSOperation子類的對象,把對象添加到NSOperationQueue隊列里執行,我們會把我們的執行操作放在NSOperation中main函數中。
3.GCD
Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法,GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。它讓程序平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務,一個任務可以是一個函數(function)或者是一個block。 dispatch queue分為下面三種: private dispatch queues,同時只執行一個任務,通常用于同步訪問特定的資源或數據。 global dispatch queue,可以并發地執行多個任務,但是執行完成的順序是隨機的。 Main dispatch queue 它是在應用程序主線程上執行任務的。 GCD 掃盲篇;
3、單利的弊端
優點:
1:一個類只被實例化一次,提供了對唯一實例的受控訪問。
2:節省系統資源
3:允許可變數目的實例。
缺點:
1:一個類只有一個對象,可能造成責任過重,在一定程度上違背了“單一職責原則”。
2:由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
3:濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。
4、如何把異步線程轉換成同步任務進行單元測試?
參考此篇博客 里面的信號量的解釋,Dispatch Semaphore 信號量 在項目中的應用:強制把異步任務轉換為同步任務來方便進行單元測試
5、介紹下App啟動的完成過程?
①.先加載Main函數
②.在Main函數里的 UIApplicationMain方法中,創建Application對象 創建Application的Delegate對象
③.創建主循環,代理對象開始監聽事件
④.啟動完畢會調用 didFinishLaunching方法,并在這個方法中創建UIWindow
⑤.設置UIWindow的根控制器是誰
⑥.如果有storyboard,會根據info.plist中找到應用程序的入口storyboard并加載箭頭所指的控制器
⑦.顯示窗口
本文考慮的時步驟③之后到步驟⑦結束時將要調用的方法
其中有AppDelegate,ViewController,MainView(控制器的View),ChildView(子控件的View)的18個方法
AppDelegate中的:
1.application:didFinishLaunchingWithOptions:
2.applicationDidBecomeActive:
ViewController中的:
3.loadView
4.viewDidLoad
5.load
6.initialize
7.viewWillAppear
8.viewWillLayoutSubviews
9.viewDidLayoutSubviews
10.viewDidAppear
MainView(控制器的View)中的:
11.initWithCoder(如果沒有storyboard就會調用initWithFrame,這里兩種方法視為一種)
12.awakeFromNib
13.layoutSubviews
14.drawRect
ChildView(子控件View)中的:
15.initWithCoder(如果沒有storyboard就會調用initWithFrame,這里兩種方法視為一種)
16.awakeFromNib
17.layoutSubviews
18.drawRect
那么問題來了,不往下看你可以把上面的十八個方法排個順序么?
- (void)load; //這是應用程序啟動就會調用的方法,在這個方法里寫的代碼最先調用
- (void)initialize; //這個是需要用到本類時才調用,這個方法里一般寫設置導航控制器的主題啊之類的,
//如果在后面的方法設置導航欄主題就晚了!(當然在上面的方法里也能寫)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
//這個方法里面會創建UIWindow,設置根控制器并展現,
//比如某些應用程序要加載授權頁面也是在這加,也可以設置觀察者,監聽到通知切換根控制器
ChildView - (instancetype)initWithCoder:(NSCoder *)aDecoder;
//這里反正我是萬萬沒想到,childView的initwithcoder會在MainView的方法之前調用,
//父的都還沒出來,就先整子控件? 有了解比較透徹的博友懇請告訴我謝謝。
MainView - (instancetype)initWithCoder:(NSCoder *)aDecoder;
// 就是關于應用程序的數據存儲后的解檔操作。
MainView - (void)awakeFromNib;
//在這個方法里設置view的背景等一系列普通操作,不要寫關于frame的還不準,
//在使用IB的時候才會涉及到此方法的使用,當.nib文件被加載的時候,
//會發送一個awakeFromNib的消息到.nib文件中的每個對象,
//每個對象都可以定義自己的awakeFromNib函數來響應這個消息,執行一些必要的操作。
ChildView - (void)awakeFromNib
//子控件也有本方法,重寫父類的方法。基本用法同上 - (void)loadView;
//創建視圖的層次結構,這里需要注意,
//在沒有創建控制器的view的情況下不能直接寫 self.view 因為self.view的底層是:
if(_view == nil){
_view = [self loadView]
}
//所以這么寫會直接造成死循環。
//如果重寫這個loadView方法里面什么都不寫,會顯示黑屏。 - (void)viewDidLoad;
//臥槽,這個方法是用的最多的方法,但是在之后的開發中就會發現越來越不靠譜,
//很多東西都還沒加載完畢,各種取值都不準確,很少在這里面寫東西了。
//這里只是把視圖元件加載完成 - (void)viewWillAppear:(BOOL)animated;
//視圖將要出現,這個方法用的非常多,比如如果要設置導航欄的setNavigationBarHiden:animate:
//就必須要在這里寫,才能完美契合,不卡跳。 還有很多比如監聽屏幕旋轉啦,
//viewWillTransitionToSize:可能要在本方法里再調一次,
//或者就是新到這個界面要reloadData或是自動下拉刷新等 都是寫在本方法里
- (void)viewWillLayoutSubviews;
//視圖將要布局子視圖,蘋果建議的設置界面布局屬性的方法,
//這個方法和viewWillAppear里,系統的底層都是沒有寫任何代碼的,也就是說這里面不寫super 也是可以的
MainView - (void)layoutSubviews;
//在這個方法里一般設置子控件的frame,因為這里相當于是布局基本完成了,
//設置時取到的frame或者是self.bounds才最準,如果在awakeFromeNib里寫會不準確 。
//還有這里要切記千萬不能把super layoutSubviews忘了,可能最后都很難找到這個bug - (void)viewDidLayoutSubviews;
//這個方法我也是玩玩沒想到,控制器的view的子控件還沒有布局好呢,怎么這個控制器就已經說布局全部完成了?
//那后邊的布局就不等了? 有獨到見解的也懇請你告訴我,這其中蘋果的意思到底是什么。
ChildView - (void)layoutSubviews;
//控制器的子控件里的子控件的布局就在這里寫了。
MainView - (void)drawRect:(CGRect)rect;
//因為默認所有額UI控件都是畫上去的,在這一步就是把所有的東西畫上去,
//有時候需要用到Quartz2D的知識的時候都是在這個方法里話,但也是要注意別忘了寫super,
//不然系統原本的東西就都畫不上來了,這里要建議盡可能使用貝塞爾路徑畫圖形,
//因為系統默認的那個上下文畫法有時可能會內存泄露。drawRect方法只能在加載時調用一次,
//如果后面還需要調用,比如下載進度的圓弧,需要一直刷幀,
//就要使用setNeedsDisplay來定時多次調用本方法
ChildView - (void)drawRect:(CGRect)rect;
//view的子控件內部的畫圖方法,有時可以自己自定義label 中間帶個刪除線的(用來寫打折前的原價) 就是在這里畫根線 。 - (void)viewDidAppear:(BOOL)animated;
//把上面的畫圖都畫完了,這里就會顯示,視圖完全加載完成。
//在這里的操作可能就是設置頁面的一些動畫,或者是設置tableView,collectionView,
//QQ聊天頁面啥的滾動到底部scrollToIndexPath之類的代碼操作。 - (void)applicationDidBecomeActive:(UIApplication *)application;
//最后這是AppDelegate的應用程序獲取焦點方法,真正到了這里,才是所有東西全部加載完畢,應用程序整裝待發保持最佳狀態等待用戶操作。
//這個方法中一般會寫關于彈出鍵盤的方法,比如有的用戶登錄界面為了更好的用戶體驗,
//就讓你在剛打開程序來到登錄界面的時候,光標的焦點就自動在賬號的文本框里閃爍,
//也就是設置賬號文本框為第一響應者。鍵盤在頁面加載完畢后從下方彈出,這種代碼一般就在本方法寫。
6、比如App啟動過慢,你可能想到的因素有哪些?
- App啟動過程
解析Info.plist
加載相關信息,例如如閃屏
沙箱建立、權限檢查
Mach-O加載
如果是胖二進制文件,尋找合適當前CPU類別的部分
加載所有依賴的Mach-O文件(遞歸調用Mach-O加載的方法)
定位內部、外部指針引用,例如字符串、函數等
執行聲明為attribute((constructor))的C函數
加載類擴展(Category)中的方法
C++靜態對象加載、調用ObjC的 +load 函數
程序執行
調用main()
調用UIApplicationMain()
調用applicationWillFinishLaunching
2、影響啟動性能的因素
main()函數之前耗時的影響因素
動態庫加載越多,啟動越慢。
ObjC類越多,啟動越慢
C的constructor函數越多,啟動越慢
C++靜態對象越多,啟動越慢
ObjC的+load越多,啟動越慢
main()函數之后耗時的影響因素
執行main()函數的耗時
執行applicationWillFinishLaunching的耗時
rootViewController及其childViewController的加載、view及其subviews的加載
另外參考一下今日頭條的啟動優化方案
針對于今日頭條這個App我們可以優化的點如下:
純代碼方式而不是storyboard加載首頁UI。
對didFinishLaunching里的函數考慮能否挖掘可以延遲加載或者懶加載,需要與各個業務方pm和rd共同check 對于一些已經下線的業務,刪減冗余代碼。
對于一些與UI展示無關的業務,如微博認證過期檢查、圖片最大緩存空間設置等做延遲加載。
對實現了+load()方法的類進行分析,盡量將load里的代碼延后調用。
上面統計數據顯示展示feed的導航控制器頁面(NewsListViewController)比較耗時,對于viewDidLoad以及viewWillAppear方法中盡量去嘗試少做,晚做,不做。
7、0x8badf00d表示是什么?
看門狗超時,在iOS上,它經常出現在執行一個同步網絡調用而阻塞主線程的情況。因此,永遠不要進行同步網絡調用。
8、怎么防止反編譯?
本地數據加密。
iOS應用防反編譯加密技術之一:對NSUserDefaults,sqlite存儲文件數據加密,保護帳號和關鍵信息
URL編碼加密。
iOS應用防反編譯加密技術之二:對程序中出現的URL進行編碼加密,防止URL被靜態分析
網絡傳輸數據加密。
iOS應用防反編譯加密技術之三:對客戶端傳輸數據提供加密方案,有效防止通過網絡接口的攔截獲取數據
方法體,方法名高級混淆。
iOS應用防反編譯加密技術之四:對應用程序的方法名和方法體進行混淆,保證源碼被逆向后無法解析代碼
程序結構混排加密。
iOS應用防反編譯加密技術之五:對應用程序邏輯結構進行打亂混排,保證源碼可讀性降到最低
其實我覺得大可不必,本身反編譯成本就很大,代碼這么多,一個個反編譯過來是在蛋疼,就算有偽代碼也需要理解,而且有些代碼就算有偽代碼也很難理解。
只要做好核心代碼,做好混淆就行了,比如涉及到密碼,核心算法。
9、說說你遇到的技術難點并如何解決的?
一般這種問題的時候,面試官是想通過你講解你遇到的是什么類型的問題,然后是怎么解決的方案來判斷你的知識層面。值得注意的一點是,往往不少同學在回答這道問題講如何解決的時候會說:首先通過Google、百度等搜索尋找解決方案,2、問群里。。。等等之類的答案。敲黑板啦~~注意啦,同學們,人家要的不是這個答案好嗎?雖然你這么答也不算是錯,只是理解有偏差,但起不到加分的結果。所以在面試之前,各位同學應該好好的想想這個問題的答案,當然每個人遇到的難點都不大相同,涉及到的技術層面也不一樣,這也是這個問題的目的所在。
10、說說你了解的第三方原理或底層知識?
作為一個開發者,有一個學習的氛圍和一個交流圈子特別重要,這是我的交流群(123),大家有興趣可以進群里一起交流學習
另附上一份大廠面試題合集,進群可自行下載!