iOS問答 - 2

目錄

1.解釋self = [super init]方法
2.網(wǎng)絡七層協(xié)議
3.OC中是否有二維數(shù)組,如何實現(xiàn)二維數(shù)組
4.@synthesize、@dynamic的理解
5.XIB與Storyboards及純代碼的優(yōu)缺點
6.內(nèi)存的使用和優(yōu)化的注意事項
7.pushViewController、presentViewController原理及區(qū)別.
8.發(fā)現(xiàn)VC不走dealloc,如何檢查原因。
9. 什么情況使用 weak 關鍵字,相比 assign 有什么不同?
10. objc中的類方法和實例方法有什么本質(zhì)區(qū)別和聯(lián)系?
11. _objc_msgForward函數(shù)是做什么的,直接調(diào)用它將會發(fā)生什么?
12.__block和__weak修飾符的區(qū)別:
13.iOS 中的事件的傳遞:響應鏈
14.應用程序的生命周期
15.動畫原理與實現(xiàn)
16.類結構體的組成,isa指針指向了什么?
17.RunLoop有幾種事件源?有幾種模式?
18.lldb(gdb)常用的調(diào)試命令?
19.performSelector延時調(diào)用導致的內(nèi)存泄露?
20.如何對真機的crash日志進行分析?
21.對象回收時Weak指針自動被置為nil的實現(xiàn)原理
22.遠程推送
23.熱修復
24.組件化開發(fā)
25.Instruments性能檢測
26.加密
27.優(yōu)化CPU消耗
28.單例
29.Objective-C堆和棧的區(qū)別?
30.關鍵字const有什么含意?修飾類呢?static的作用,用于類呢?還有extern c的作用
31.關鍵字volatile有什么含意?并給出三個不同的例子。
32. 一個參數(shù)既可以是const還可以是volatile嗎? 一個指針可以是volatile 嗎?解釋為什么。
33.static 關鍵字的作用:
34.線程與進程的區(qū)別和聯(lián)系?
35.列舉幾種進程的同步機制,并比較其優(yōu)缺點。
36. 進程之間通信的途徑
37. 進程死鎖的原因
38. 死鎖的4個必要條件
39. 死鎖的處理
40.cocoa touch框架
41.自動釋放池是什么,如何工作
42. Objective-C的優(yōu)缺點。
43.sprintf,strcpy,memcpy使用上有什么要注意的地方。
44.http和scoket通信的區(qū)別。
45.TCP和UDP的區(qū)別
46.請簡要說明viewDidLoad和viewDidUnload何時調(diào)用
47. 簡述內(nèi)存分區(qū)情況
48.隊列和棧有什么區(qū)別:
49.HTTP協(xié)議中,POST和GET的區(qū)別是什么?
50.iOS的系統(tǒng)架構

1.解釋self = [super init]方法

容錯處理,當父類初始化失敗,會返回一個nil,表示初始化失敗。由于繼承的關系,子類是需要擁有父類的實例和行為,因此,我們必須先初始化父類,然后再初始化子類

2.網(wǎng)絡七層協(xié)議

應用層:
1.用戶接口、應用程序;
2.Application典型設備:網(wǎng)關;
3.典型協(xié)議、標準和應用:TELNET、FTP、HTTP
表示層:
1.數(shù)據(jù)表示、壓縮和加密presentation
2.典型設備:網(wǎng)關
3.典型協(xié)議、標準和應用:ASCLL、PICT、TIFF、JPEG|MPEG
4.表示層相當于一個東西的表示,表示的一些協(xié)議,比如圖片、聲音和視頻MPEG。
會話層:
1.會話的建立和結束;
2.典型設備:網(wǎng)關;
3.典型協(xié)議、標準和應用:RPC、SQL、NFS、X WINDOWS、ASP
傳輸層:
1.主要功能:端到端控制Transport;
2.典型設備:網(wǎng)關;
3.典型協(xié)議、標準和應用:TCP、UDP、SPX
網(wǎng)絡層:
1.主要功能:路由、尋址Network;
2.典型設備:路由器;
3.典型協(xié)議、標準和應用:IP、IPX、APPLETALK、ICMP;
數(shù)據(jù)鏈路層:
1.主要功能:保證無差錯的疏忽鏈路的data link;
2.典型設備:交換機、網(wǎng)橋、網(wǎng)卡;
3.典型協(xié)議、標準和應用:802.2、802.3ATM、HDLC、FRAME RELAY;
物理層:
1.主要功能:傳輸比特流Physical;
2.典型設備:集線器、中繼器
3.典型協(xié)議、標準和應用:V.35、EIA/TIA-232.

3.OC中是否有二維數(shù)組,如何實現(xiàn)二維數(shù)組

OC中沒有二維數(shù)組,可通過嵌套數(shù)組實現(xiàn)二維數(shù)組。

4.@synthesize、@dynamic的理解

@synthesize是系統(tǒng)自動生成getter和setter屬性聲明;@synthesize的意思是,除非開發(fā)人員已經(jīng)做了,否則由編譯器生成相應的代碼,以滿足屬性聲明;
@dynamic是開發(fā)者自已提供相應的屬性聲明,@dynamic意思是由開發(fā)人員提供相應的代碼:對于只讀屬性需要提供setter,對于讀寫屬性需要提供 setter 和getter。查閱了一些資料確定@dynamic的意思是告訴編譯器,屬性的獲取與賦值方法由用戶自己實現(xiàn), 不自動生成。

5.XIB與Storyboards及純代碼的優(yōu)缺點

優(yōu)點:
XIB:在編譯前就提供了可視化界面,可以直接拖控件,也可以直接給控件添加約束,更直觀一些,而且類文件中就少了創(chuàng)建控件的代碼,確實簡化不少,通常每個XIB對應一個類。
Storyboard:在編譯前提供了可視化界面,可拖控件,可加約束,在開發(fā)時比較直觀,而且一個storyboard可以有很多的界面,每個界面對應一個類文件,通過storybard,可以直觀地看出整個App的結構。
缺點:
XIB:需求變動時,需要修改XIB很大,有時候甚至需要重新添加約束,導致開發(fā)周期變長。XIB載入相比純代碼自然要慢一些。對于比較復雜邏輯控制不同狀態(tài)下顯示不同內(nèi)容時,使用XIB是比較困難的。當多人團隊或者多團隊開發(fā)時,如果XIB文件被發(fā)動,極易導致沖突,而且解決沖突相對要困難很多。
Storyboard:需求變動時,需要修改storyboard上對應的界面的約束,與XIB一樣可能要重新添加約束,或者添加約束會造成大量的沖突,尤其是多團隊開發(fā)。對于復雜邏輯控制不同顯示內(nèi)容時,比較困難。當多人團隊或者多團隊開發(fā)時,大家會同時修改一個storyboard,導致大量沖突,解決起來相當困難。

手寫代碼:
優(yōu)勢:適合大型項目大規(guī)模使用,利于版本管理、追蹤改動以及代碼合并;最好的代碼重用性
劣勢:慢,開發(fā)周期長,維護代碼復雜;自動布局AutoLayout困難
xib:
優(yōu)勢:開發(fā)速度快;在版本管理上和純代碼的差異并不是很大,易讀易維護
劣勢:xib中的設置往往并非最終設置,UI設計會被代碼所覆蓋;
storyboard:
優(yōu)勢:可以看到每個ViewController的布局樣式,也可以明確地知道各個ViewController之間的轉換關系;代碼量少,開發(fā)周期短;關鍵是已經(jīng)成為新建項目時候的默認配置,代表著蘋果以后的方向和重心
劣勢:很難多人協(xié)作;ViewController的重用和自定義的view的處理;

6.內(nèi)存的使用和優(yōu)化的注意事項

  • 重用問題:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews設置正確的reuseIdentifier,充分重用;
  • 盡量把views設置為不透明:當opque為NO的時候,圖層的半透明取決于圖片和其本身合成的圖層為結果,可提高性能;
  • 不要使用太復雜的XIB/Storyboard:載入時就會將XIB/storyboard需要的所有資源,包括圖片全部載入內(nèi)存,即使未來很久才會使用。那些相比純代碼寫的* 延遲加載,性能及內(nèi)存就差了很多;
  • 選擇正確的數(shù)據(jù)結構:學會選擇對業(yè)務場景最合適的數(shù)組結構是寫出高效代碼的基礎。比如,數(shù)組: 有序的一組值。使用索引來查詢很快,使用值查詢很慢,插入/刪除很慢。字典: 存儲鍵值對,用鍵來查找比較快。集合: 無序的一組值,用值來查找很快,插入/刪除很快。
  • gzip/zip壓縮:當從服務端下載相關附件時,可以通過gzip/zip壓縮后再下載,使得內(nèi)存更小,下載速度也更快。
  • 延遲加載:對于不應該使用的數(shù)據(jù),使用延遲加載方式。對于不需要馬上顯示的視圖,使用延遲加載方式。比如,網(wǎng)絡請求失敗時顯示的提示界面,可能一直都不會使用到,因此應該使用延遲加載。
  • 數(shù)據(jù)緩存:對于cell的行高要緩存起來,使得reload數(shù)據(jù)時,效率也極高。而對于那些網(wǎng)絡數(shù)據(jù),不需要每次都請求的,應該緩存起來,可以寫入數(shù)據(jù)庫,也可以通過plist文件存儲。
  • 處理內(nèi)存警告:一般在基類統(tǒng)一處理內(nèi)存警告,將相關不用資源立即釋放掉
  • 重用大開銷對象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它們。通常是作為屬性存儲起來,防止反復創(chuàng)建。
  • 避免反復處理數(shù)據(jù):許多應用需要從服務器加載功能所需的常為JSON或者XML格式的數(shù)據(jù)。在服務器端和客戶端使用相同的數(shù)據(jù)結構很重要;
  • 使用Autorelease Pool:在某些循環(huán)創(chuàng)建臨時變量處理數(shù)據(jù)時,自動釋放池以保證能及時釋放內(nèi)存;
  • 正確選擇圖片加載方式

7.pushViewController、presentViewController原理及區(qū)別.

區(qū)別:present只能逐級返回,push所有視圖由視圖??刂?,可以返回上一級,也可以返回到根vc,其他vc。present一般用于不同業(yè)務界面的切換,push一般用于同一業(yè)務不同界面之間的切換。

8.發(fā)現(xiàn)VC不走dealloc,如何檢查原因。

在一個項目中,如果ViewController使用完成之后,發(fā)現(xiàn)這個東東并沒有釋放掉,dealloc方法不走,看著那個內(nèi)存蹭蹭的網(wǎng)上增,相信大家都知道如何去釋放一個不用的ViewController,但是還是有些其他因素限制了內(nèi)存釋放。

如果你的VC中有NSTimer,那么就要注意了,因為當你[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];時,這個 target:self 就增加了VC的RetarnCountr如果你不將這個timer invalidate,就別想調(diào)用dealloc。

再然后,一個比較隱蔽的因素,你反過頭去找找看,跟這個類有關的代理,嗯,對是代理,有沒有強引用的屬性???對,比如一個代理的delegate應

該是 assign 的現(xiàn)在是retain,就是這個,它會影響你不讓你調(diào)用dealloc,不信,就試試吧。

最后,如果以上都沒有問題的話,那么,真問題就來了。我就遇到了這種情況,在使用ASI進行網(wǎng)絡請求的時候,因為需求原因,我使用屬性將名為ASIFormDataRequest 的NSOperation 標記住了,就將上面的問題找了又找,就是不行,最后是將那個標記的屬性置為 nil,才解決了這個不調(diào)用 dealloc的這個蛋疼問題。所以,如果你遇到了比較隱蔽的原因,那就去找找你自己控制不了的因素,就像這個第三方。如果你不了解它的運行機制,那就一定要注意這個庫

PS:dealloc中的釋放也是有順序的,就好比創(chuàng)建時,先父類,再子類,釋放的時候反過來,不然有幾率會crash,至于原因。 子類是父類的繼承,比較NB,以至于要殺死他們的時候應該先干掉比較牛B的子類。

9. 什么情況使用 weak 關鍵字,相比 assign 有什么不同?

什么情況使用 weak 關鍵字?
1)在ARC中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用weak來解決,比如:delegate代理屬性
2)自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用weak,自定義IBOutlet控件屬性一般也使用weak;當然,也可以使用strong。在下文也有論述:《IBOutlet連出來的視圖屬性為什么可以被設置成weak?》
不同點:
1)weak 此特質(zhì)表明該屬性定義了一種“非擁有關系” (nonowning relationship)。為這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值。此特質(zhì)同assign類似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。 而 assign 的“設置方法”只會執(zhí)行針對“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作。
2)assigin 可以用非OC對象,而weak必須用于OC對象

10. objc中的類方法和實例方法有什么本質(zhì)區(qū)別和聯(lián)系?

類方法:
類方法是屬于類對象的
類方法只能通過類對象調(diào)用
類方法中的self是類對象
類方法可以調(diào)用其他的類方法
類方法中不能訪問成員變量
類方法中不能直接調(diào)用對象方法

實例方法:
實例方法是屬于實例對象的
實例方法只能通過實例對象調(diào)用
實例方法中的self是實例對象
實例方法中可以訪問成員變量
實例方法中直接調(diào)用實例方法
實例方法中也可以調(diào)用類方法(通過類名)

11. _objc_msgForward函數(shù)是做什么的,直接調(diào)用它將會發(fā)生什么?

_objc_msgForward是 IMP 類型,用于消息轉發(fā)的:當向一個對象發(fā)送一條消息,但它并沒有實現(xiàn)的時候,_objc_msgForward會嘗試做消息轉發(fā)。

12.__block和__weak修飾符的區(qū)別:

__block不管是ARC還是MRC模式下都可以使用,可以修飾對象,還可以修飾基本數(shù)據(jù)類型。
__weak只能在ARC模式下使用,也只能修飾對象(NSString),不能修飾基本數(shù)據(jù)類型(int)。
__block對象可以在block中被重新賦值,__weak不可以。

13.iOS 中的事件的傳遞:響應鏈

簡要說一下:
事件沿著一個指定的路徑傳遞直到它遇見可以處理它的對象。 首先一個UIApplication 對象從隊列頂部獲取一個事件并分發(fā)(dispatches)它以便處理。 通常,它把事件傳遞給應用程序的關鍵窗口對象,該對象把事件傳遞給一個初始對象來處理。 初始對象取決于事件的類型。

觸摸事件。 對于觸摸事件,窗口對象首先嘗試把事件傳遞給觸摸發(fā)生的視圖。那個視圖被稱為hit-test(點擊測試)視圖。 尋找hit-test視圖的過程被稱為hit-testing, 參見 “Hit-Testing Returns the View Where a Touch Occurred.”

運動和遠程控制事件。 對于這些事件,窗口對象把shaking-motion(搖晃運動)或遠程控制事件傳遞給第一響應者來處理。第一響應者請參見 “The Responder Chain Is Made Up of Responder Objects.”

iOS 使用hit-testing來找到事件發(fā)生的視圖。 Hit-testing包括檢查觸摸事件是否發(fā)生在任何相關視圖對象的范圍內(nèi), 如果是,則遞歸地檢查所有視圖的子視圖。在視圖層次中的最底層視圖,如果它包含了觸摸點,那么它就是hit-test視圖。等 iOS決定了hit-test視圖之后,它把觸摸事件傳遞給該視圖以便處理。

14.應用程序的生命周期

一個應用程序有五種狀態(tài):

  • Not running 未運行 ,程序沒啟動。
  • Inactive 未激活 ,程序在前臺運行,沒有接收到事件。在程序沒有事件需要處理時停留在這個狀態(tài)。
  • Active 激活,程序在前臺運行而且接收到了事件。這也是前臺的一個正常的模式
  • Backgroud 后臺,程序在后臺而且能執(zhí)行代碼,大多數(shù)程序只能短暫停留這個狀態(tài),馬上進入Suspended狀態(tài)。
  • Suspended 掛起,程序在后臺不能執(zhí)行代碼。但程序不會被馬上殺死,當系統(tǒng)內(nèi)存不足時,在這個狀態(tài)的程序占用的內(nèi)存優(yōu)先被回收。

切換狀態(tài)時的回調(diào):
在發(fā)生狀態(tài)切換時,都會調(diào)用delegate對象對應的方法來響應App狀態(tài)的改變。

  • application:willFinishLaunchingWithOptions: 這個方法是你在啟動時的第一次機會來執(zhí)行代碼
  • application:didFinishLaunchingWithOptions: 這個方法允許你在顯示app給用戶之前執(zhí)行最后的初始化操作
  • applicationDidBecomeActive: app已經(jīng)切換到active狀態(tài)后需要執(zhí)行的操作
  • applicationWillResignActive: app將要從前臺切換到后臺時需要執(zhí)行的操作
  • applicationDidEnterBackground: app已經(jīng)進入后臺后需要執(zhí)行的操作
  • applicationWillEnterForeground: app將要從后臺切換到前臺需要執(zhí)行的操作,但app還不是active狀態(tài)
  • applicationWillTerminate: app將要結束時需要執(zhí)行的操作

接下來是App啟動、切換和鎖屏狀態(tài)時調(diào)用delegate對象的方法

  • App啟動
    App啟動時,首先由not running狀態(tài)切換到inactive狀態(tài),此時調(diào)用application:didFinishLaunchingWithOptions:方法;然后調(diào)用application:didFinishLaunchingWithOptions:方法,最后由inactive狀態(tài)切換到active狀態(tài),此時調(diào)用applicationDidBecomeActive:方法。
  • App無事件響應
    由active狀態(tài)切換到inactive狀態(tài),此時調(diào)用applicationWillResignActive:方法。
  • 切換App
    當切換到另一個App時,由狀態(tài)active切換到inactive,此時調(diào)用applicationWillResignActive:方法;然后從inactive狀態(tài)切換到running(background)狀態(tài),此時調(diào)用applicationDidEnterBackground:方法。
  • 鎖屏
    當手機鎖屏時,由狀態(tài)active切換到inactive,此時調(diào)用applicationWillResignActive:;然后再由inactive狀態(tài)切換到running(background)狀態(tài),此時調(diào)用applicationDidEnterBackground:方法。
  • App響應中斷
    當一個基于警告式的中斷發(fā)生時,比如有電話打進來了,這是程序會臨時進入inactive狀態(tài),這用戶可以選擇如何處理這個中斷。接著會調(diào)用applicationWillResignActive:方法,當中斷來臨時,你需要在這個方法中,停止timer或者周期性任務、停止視頻,音樂播放、停止游戲運行。當程序回到active狀態(tài) , applicationDidBecomeActive: 會調(diào)用方法,恢復停止的操作。
  • App轉到后臺運行
    首先調(diào)用applicationWillResignActive:方法,程序即將進入后臺運行,接著調(diào)用applicationDidEnterBackground: 方法,此時程序為background狀態(tài),系統(tǒng)允許程序繼續(xù)運行一段時間,然后程序進入Suspended狀態(tài)。
  • App轉到前臺運行
    系統(tǒng)喚醒程序,調(diào)用applicationWillEnterForeground: 方法,程序從background狀態(tài)改為active狀態(tài),接著調(diào)用applicationDidBecomeActive:方法。當app處于掛起狀態(tài)時,它是不能執(zhí)行任何代碼的。因此它不能處理在掛起期間發(fā)過來的通知,比如方向改變,時間改變,設置的改變還有其他影響程序展現(xiàn)的或狀態(tài)的通知。在程序返回后臺或前臺時,程序要正確的處理這些通知。
  • App終止
    當App被系統(tǒng)終止(如內(nèi)存不足、Crash)或者用戶自行終止。系統(tǒng)會在應用程序終止之前調(diào)用applicationWillTerminate: 方法,來保存用戶的一些重要數(shù)據(jù)以便下次啟動時恢復到app原來的狀態(tài)。

15.動畫原理與實現(xiàn)

動畫的實現(xiàn)
我們也可將 iOS 的動畫分為兩大類:
系統(tǒng)提供的 關鍵幀動畫 實現(xiàn)方式;用戶指定 關鍵 信息,系統(tǒng)實現(xiàn)動畫過程,對用戶而言操作起來會簡單些。
逐幀動畫 實現(xiàn)方式;用戶自己 畫 出每一幀畫面,系統(tǒng)操作方法簡單,但用戶操作的工作量就會大一些。

逐幀動畫實現(xiàn)方式

簡單的說,要實現(xiàn)逐幀的方式,就是需要 周期性 的調(diào)用 繪制 方法,繪制每幀的動畫對象。
這里說的 繪制,不光是指覆寫 UIView 的 - drawRect:的方法來手動重繪視圖,也包括修改 UIView 它的屬性,比如位置、顏色等。
iOS 的動畫都是基于 CALayer 的,iOS 的 UIView 背后都有一個對應的 CALayer 。對 UIView 的修改實際上都是對背后 CALayer 的修改。
但如果在逐幀繪制的方法中修改了一個自建的 CALayer,這個 CALayer 不是對應某個 UIView 的,需注意系統(tǒng)的 隱式動畫 的影響,后面會提到這點。
而 周期性,就需要一個定時器來完成了,即 CADisplayLink。
CADisplayLink 與 NSTimer 比較類似,可以周期性的調(diào)用指定的方法。
之所以用 CADisplayLink,是因為它是基于屏幕刷新率的,即屏幕每次刷新時就會觸發(fā)調(diào)用。
iPhone 的屏幕刷新率是 60 FPS。

關鍵幀動畫實現(xiàn)方式

采用關鍵幀的方式來實現(xiàn)動畫,要講的內(nèi)容相對逐幀的方式就多的多了。
還是用 UIView 移動的簡單例子。
這里面有兩個關鍵幀,起始幀和結束幀,除此之外還有2個關鍵信息:
起始幀,變化信息:坐標為 (0,0)
結束幀,變化信息:坐標為 (100,0)
動畫時間,0.25秒
勻速運動
坐標 信息是 UIView 的一個屬性(實際是對應到 CALayer 的屬性),在動畫實現(xiàn)里,我們只需要指定起始和結束的兩個關鍵值就夠了,中間的過渡值都有系統(tǒng)自動生成。
這里出現(xiàn)了兩種值,一個是我們設定的,一個是系統(tǒng)生成的,所以要先在這里插入一個 模型層 和 展現(xiàn)層 的概念了
CALayer 的同一個屬性值,會分別保存在模型層 modelLayer ,和展現(xiàn)層 presentationLayer 中。當我們修改屬性值時,是修改的模型層的數(shù)值,動畫時系統(tǒng)根據(jù)模型層的變化,生成的過渡值,是保存在展現(xiàn)層中的。
在 CALayer 的對象里能直接訪問到這兩層的信息。
而 CALayer 的底層實現(xiàn)實際不止這兩層,但我們現(xiàn)在討論動畫的時候,可以只關心這兩層。

動畫實現(xiàn)

將動畫加入 CALayer 的代碼定義為:

- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key

接受的類型是 CAAnimation 類型,有下面這些子類:
CABasicAnimation,可設定起始結束兩個關鍵幀的信息。
CAKeyframeAnimation,除首尾外,還可添加多個中間關鍵點。
CAAnimationGroup ,可組合多個動畫,因為上面兩種動畫一次只能設置一個屬性值。
CATransition,圖層過渡動畫,默認是淡入。比如修改一個 CALayer的背景色時,是從初始色慢慢淡入過渡到結束色。
可修改為新顏色把舊顏色頂出去等效果。還可使用 CIFilter 濾鏡做過渡效果,一些開源 UIViewController 的過渡動畫使用了這種方式。
動畫中,除了屬性值外,我們還設置了兩個和時間有關的信息:動畫時間0.25秒,運動方式是勻速運動。
動畫持續(xù)時間很簡單,是通過 CAAnimation 遵守的 CAMediaTiming 協(xié)議設定的。
勻速運動是通過設置 CAAnimation 的 timingFunction 實現(xiàn)的,這是一個 CAMediaTimingFunction 類的對象。

隱式動畫

上面的過程,我們是 顯式 的向一個 CALayer 添加了一個動畫,所以這種方式叫做 顯式動畫。
對應的,還有 隱式動畫,即系統(tǒng)自動添加上的動畫。

    CALayer *layer = [CALayer layer];
    layer.backgroundColor = [UIColor greenColor].CGColor;
    layer.frame = CGRectMake(0, 0, 100, 100);
    [self.view.layer addSublayer:layer];
    layer.frame = CGRectOffset(layer.frame, 100, 0);

這段代碼里,我們沒有添加 CAAnimation 動畫,但 layer 不是直接變化到新的位置,而是有一個動畫效果。
這就是 隱式動畫 的效果。

動畫事務

創(chuàng)建動畫事務的目的是為了操作的原子性,保證動畫的所有修改能同時生效。
CATransaction 就是動畫事務的操作類。
在創(chuàng)建隱式動畫的時候,系統(tǒng)也會隱式的創(chuàng)建一個動畫事務,以保證所有的動畫能同時進行。
除此之外,還可以顯式的創(chuàng)建一個事務。
顯式事務中可以定義事務中所有動畫的運行時間和時間函數(shù),此外,還有這個方法 + (void)setDisableActions:(BOOL)flag 能顯式的關閉這個事務中的 action 查詢操作。
關閉了查詢也就是關閉了動畫效果,屬性值的變化就會立即生效,而沒有動畫效果了:

    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    ///...
    layer.frame = CGRectOffset(layer.frame, 100, 0);
    ///...
    [CATransaction commit];

16.類結構體的組成,isa指針指向了什么?

在Objective-C中,任何類的定義都是對象。類和類的實例(對象)沒有任何本質(zhì)上的區(qū)別。任何對象都有isa指針。
Class 是一個 objc_class 結構類型的指針, id是一個 objc_object 結構類型的指針.

objc_class 的定義:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

各個參數(shù)的意思:
isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向對象的類,而Class里也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表。當類方法被調(diào)用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass).根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)。
super_class:父類,如果該類已經(jīng)是最頂層的根類,那么它為NULL。
version:類的版本信息,默認為0
info:供運行期使用的一些位標識。
instance_size:該類的實例變量大小
ivars:成員變量的數(shù)組

每一個對象本質(zhì)上都是一個類的實例。其中類定義了成員變量和成員方法的列表。對象通過對象的isa指針指向類。
每一個類本質(zhì)上都是一個對象,類其實是元類(meteClass)的實例。元類定義了類方法的列表。類通過類的isa指針指向元類。
所有的元類最終繼承一個根元類,根元類isa指針指向本身,形成一個封閉的內(nèi)循環(huán)。

17.RunLoop有幾種事件源?有幾種模式?

輸入事件來源

Run loop接收輸入事件來自兩種不同的來源:輸入源(input source)和定時源(timer source)。兩種源都使用程序的某一特定的處理例程來處理到達的事件。
當你創(chuàng)建輸入源,你需要將其分配給run loop中的一個或多個模式(什么是模式,下文將會講到)。模式只會在特定事件影響監(jiān)聽的源。大多數(shù)情況下,run loop運行在默認模式下,但是你也可以使其運行在自定義模式。若某一源在當前模式下不被監(jiān)聽,那么任何其生成的消息只在run loop運行在其關聯(lián)的模式下才會被傳遞。

輸入源(input source)
傳遞異步事件,通常消息來自于其他線程或程序。輸入源傳遞異步消息給相應的處理例程,并調(diào)用runUntilDate:方法來退出(在線程里面相關的NSRunLoop對象調(diào)用)

基于端口的輸入源
基于端口的輸入源由內(nèi)核自動發(fā)送。
Cocoa和Core Foundation內(nèi)置支持使用端口相關的對象和函數(shù)來創(chuàng)建的基于端口的源。例如,在Cocoa里面你從來不需要直接創(chuàng)建輸入源。你只要簡單的創(chuàng)建端口對象,并使用NSPort的方法把該端口添加到run loop。端口對象會自己處理創(chuàng)建和配置輸入源。
在Core Foundation,你必須人工創(chuàng)建端口和它的run loop源。我們可以使用端口相關的函數(shù)(CFMachPortRef,CFMessagePortRef,CFSocketRef)來創(chuàng)建合適的對象。下面的例子展示了如何創(chuàng)建一個基于端口的輸入源,將其添加到run loop并啟動:

voidcreatePortSource()
{
    CFMessagePortRef port = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("com.someport"),myCallbackFunc, NULL, NULL);

    CFRunLoopSourceRef source =  CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, port, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
    while (pageStillLoading) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        CFRunLoopRun();
        [pool release];
    }
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}

自定義輸入源
自定義的輸入源需要人工從其他線程發(fā)送。
為了創(chuàng)建自定義輸入源,必須使用Core Foundation里面的CFRunLoopSourceRef類型相關的函數(shù)來創(chuàng)建。你可以使用回調(diào)函數(shù)來配置自定義輸入源。Core Fundation會在配置源的不同地方調(diào)用回調(diào)函數(shù),處理輸入事件,在源從run loop移除的時候清理它。
除了定義在事件到達時自定義輸入源的行為,你也必須定義消息傳遞機制。源的這部分運行在單獨的線程里面,并負責在數(shù)據(jù)等待處理的時候傳遞數(shù)據(jù)給源并通知它處理數(shù)據(jù)。消息傳遞機制的定義取決于你,但最好不要過于復雜。創(chuàng)建并啟動自定義輸入源的示例如下:

voidcreateCustomSource()
{
    CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

    CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);

    while (pageStillLoading) {

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        CFRunLoopRun();
        [pool release];
    }
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}

Cocoa上的Selector源
除了基于端口的源,Cocoa定義了自定義輸入源,允許你在任何線程執(zhí)行selector方法。和基于端口的源一樣,執(zhí)行selector請求會在目標線程上序列化,減緩許多在線程上允許多個方法容易引起的同步問題。不像基于端口的源,一個selector執(zhí)行完后會自動從run loop里面移除。
當在其他線程上面執(zhí)行selector時,目標線程須有一個活動的run loop。對于你創(chuàng)建的線程,這意味著線程在你顯式的啟動run loop之前是不會執(zhí)行selector方法的,而是一直處于休眠狀態(tài)。
NSObject類提供了類似如下的selector方法:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)argwaitUntilDone:(BOOL)wait modes:(NSArray *)array;

定時源(timer source)
定時源在預設的時間點同步方式傳遞消息,這些消息都會發(fā)生在特定時間或者重復的時間間隔。定時源則直接傳遞消息給處理例程,不會立即退出run loop。
需要注意的是,盡管定時器可以產(chǎn)生基于時間的通知,但它并不是實時機制。和輸入源一樣,定時器也和你的run loop的特定模式相關。如果定時器所在的模式當前未被run loop監(jiān)視,那么定時器將不會開始直到run loop運行在相應的模式下。類似的,如果定時器在run loop處理某一事件期間開始,定時器會一直等待直到下次run loop開始相應的處理程序。如果run loop不再運行,那定時器也將永遠不啟動。
創(chuàng)建定時器源有兩種方法:

方法一:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4.0
                                                     target:self                                        selector:@selector(backgroundThreadFire:) userInfo:nil

                                                    repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timerforMode:NSDefaultRunLoopMode];

方法二:
[NSTimer scheduledTimerWithTimeInterval:10
                                        target:self

                                   selector:@selector(backgroundThreadFire:)

                                       userInfo:nil

                                       repeats:YES];

Cocoa中的預定義模式有:

  • Default模式
    定義:NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)
    描述:默認模式中幾乎包含了所有輸入源(NSConnection除外),一般情況下應使用此模式。
  • Connection模式
    定義:NSConnectionReplyMode(Cocoa)
    描述:處理NSConnection對象相關事件,系統(tǒng)內(nèi)部使用,用戶基本不會使用。
  • Modal模式
    定義:NSModalPanelRunLoopMode(Cocoa)
    描述:處理modal panels事件。
  • Event tracking模式
    定義:UITrackingRunLoopMode(iOS) NSEventTrackingRunLoopMode(cocoa)
    描述:在拖動loop或其他user interface tracking loops時處于此種模式下,在此模式下會限制輸入事件的處理。例如,當手指按住UITableView拖動時就會處于此模式。
  • Common模式
    定義:NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)
    描述:這是一個偽模式,其為一組run loop mode的集合,將輸入源加入此模式意味著在Common Modes中包含的所有模式下都可以處理。在Cocoa應用程序中,默認情況下Common Modes包含default modes,modal modes,event Tracking modes.可使用CFRunLoopAddCommonMode方法想Common Modes中添加自定義modes。

Run loop的優(yōu)點:
首先,NSRunLoop是一種更加高明的消息處理模式,他就高明在對消息處理過程進行了更好的抽象和封裝,這樣才能是的你不用處理一些很瑣碎很低層次的具體消息的處理,在NSRunLoop中每一個消息就被打包在input source或者是timer source(見后文)中了。
其次,也是很重要的一點,使用run loop可以使你的線程在有工作的時候工作,沒有工作的時候休眠,這可以大大節(jié)省系統(tǒng)資源。

18.lldb(gdb)常用的調(diào)試命令?

po:打印對象,會調(diào)用對象description方法。是print-object的簡寫
expr:可以在調(diào)試時動態(tài)執(zhí)行指定表達式,并將結果打印出來,很有用的命令
print:也是打印命令,需要指定類型
bt:打印調(diào)用堆棧,是thread backtrace的簡寫,加all可打印所有thread的堆棧
br l:是breakpoint list的簡寫

19.performSelector延時調(diào)用導致的內(nèi)存泄露

最后的最后才發(fā)現(xiàn)實際上是performSelector延時調(diào)用的問題,經(jīng)查找資料,performSelector關于內(nèi)存管理的執(zhí)行原理是這樣的執(zhí)行 [self performSelector:@selector(method1:) withObject:self.tableLayer afterDelay:3]; 的時候,系統(tǒng)會將tableLayer的引用計數(shù)加1,執(zhí)行完這個方法時,還會將tableLayer的引用計數(shù)減1,而在我的游戲里這個延時執(zhí)行函數(shù)是被多次調(diào)用的,有時切換場景時延時函數(shù)已經(jīng)被調(diào)用但還沒有執(zhí)行,這時tableLayer的引用計數(shù)沒有減少到0,也就導致了切換場景dealloc方法沒有被調(diào)用,出現(xiàn)了內(nèi)存泄露。

所以最后我的解決辦法就是取消那些還沒有來得及執(zhí)行的延時函數(shù),代碼很簡單:
[NSObject cancelPreviousPerformRequestsWithTarget:self]
當然你也可以一個一個得這樣用:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]

加上了這個以后,切換場景也就很順利地執(zhí)行了dealloc方法,至此問題解決!
最后在找資料時也發(fā)現(xiàn)了,延時調(diào)用實現(xiàn)長按鈕的實現(xiàn)思路,記錄下來以備后用:
在touchBegan里面

[self performSelector:@selector(longPressMethod:) withObject:nil afterDelay:longPressTime]

然后在end 或cancel里做判斷,如果時間不夠長按的時間調(diào)用:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(longPressMethod:) object:nil]

取消began里的方法

最后最后總結:
performSelector是一個很有用的函數(shù),跟它打過不少交道,經(jīng)過血與淚的教訓,總結一下它的使用如下:
使用前先檢測一下,

SEL testSelector = @selector(test:);   
 if([tester respondsToSelector:testSelector])  
  {  
          //如果響應就執(zhí)行
          [tester test:@"invoke test method"];  
  }

使用后,如果有必要,需要顯示的調(diào)用cancelPreviousPerformRequestsWithTarget:selector:object: ,否則有可能產(chǎn)生內(nèi)存泄露,而且這種內(nèi)存泄露很難發(fā)現(xiàn),因為它并不違反任何規(guī)則,所以一定要注意!

20.如何對真機的crash日志進行分析?

獲取真機crash日志

連接真機
找到Xcode --> Window --> Devices
獲取所有的crash日志文件
右鍵可以Export,就可以查看相關的crash的原因

分析crash日志

有如下3種方法

方法1 使用XCode
這種方法可能是最容易的方法了。
需要使用Xcode符號化 crash log,你需要下面所列的3個文件:

  1. crash報告(.crash文件)
  2. 符號文件 (.dsymb文件)
  3. 應用程序文件 (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"文件放到同一個目錄下?,F(xiàn)在打開終端(Terminal)然后輸入如下的命令:
exportDEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
然后輸入命令:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrashappName.crashappName.app>appName.log
現(xiàn)在,符號化的crash log就保存在appName.log中了。

方法3 使用命令行工具atos
如果你有多個“.ipa”文件,多個".dSYMB"文件,你并不太確定到底“dSYMB”文件對應哪個".ipa"文件,那么,這個方法就非常適合你。

特別當你的應用發(fā)布到多個渠道的時候,你需要對不同渠道的crash文件,寫一個自動化的分析腳本的時候,這個方法就極其有用。
這里先介紹一個概念:UUID
什么是UUID
每一個可執(zhí)行程序都有一個build UUID來唯一標識。Crash日志包含發(fā)生crash的這個應用(app)的 build UUID以及crash發(fā)生的時候,應用加載的所有庫文件的[build UUID]。

那如何知道crash文件的UUID呢?
可以用:
grep"appNamearmv"crash
或者
grep--after-context=2"BinaryImages:"
crash
可以得到類似如下的結果:
appName.crash-0x4000-0x9e7fffappNamearmv7<8bdeaf1a0b233ac199728c2a0ebb4165>/var/mobile/Applications/A0F8AB29-35D1-4E6E-84E2-954DE7D21CA1/appName.crash.app/appName
(請注意這里的0x4000,是模塊的加載地址,后面用atos的時候會用到)
如何找到app的UUID
可以使用命令:
xcrundwarfdump-–uuid
比如:
xcrundwarfdump--uuidappName.app/appName
結果如下:
UUID:8BDEAF1A-0B23-3AC1-9972-8C2A0EBB4165(armv7)appName.app/appNameUUID:5EA16BAC-BB52-3519-B218-342455A52E11(armv7s)appName.app/appName
這個app有2個UUID,表明它是一個fat binnary。
它能利用最新硬件的特性,又能兼容老版本的設備。
對比上面crash文件和app文件的UUID,發(fā)現(xiàn)它們是匹配的:
8BDEAF1A-0B23-3AC1-9972-8C2A0EBB4165
用atos命令來符號化某個特定模塊加載地址
命令是:
atos[-oAppName.app/AppName][-lloadAddress][-archarchitecture]
親測,下面3種都可以:
xcrunatos-oappName.app.dSYM/Contents/Resources/DWARF/appName-l0x4000-archarmv7xcrunatos-oappName.app.dSYM/Contents/Resources/DWARF/appName-archarmv7
xcrunatos-oappName.app/appName-archarmv7
(注:這3行選任意一行執(zhí)行都可以達到目的,其中0x4000是模塊的加載地址,從上面的章節(jié)可以找到如何得到這個地址。)
文章開頭提到crash文件中有如下兩行,
3appName0x000f462a0x4000+9846184appName0x00352aee0x4000+3468014
在執(zhí)行了上面的:
xcrunatos-oappName.app.dSYM/Contents/Resources/DWARF/appName-l0x4000-archarmv7
之后,輸入如下地址:
0x00352aee

(crash文件中的第4行:4 appName 0x00352aee 0x4000 + 3468014)
可以得到結果:
-UIScrollView(UITouch)touchesEnded:withEvent:(UIScrollView+UITouch.h:26)
這樣就找到了應用種到底是哪個模塊導致的crash問題。

21.對象回收時Weak指針自動被置為nil的實現(xiàn)原理

Runtime維護著一個Weak表,用于存儲指向某個對象的所有Weak指針;
Weak表是Hash表,Key是所指對象的地址,Value是Weak指針地址的數(shù)組;
在對象被回收的時候,經(jīng)過層層調(diào)用,會最終觸發(fā)下面的方法將所有Weak指針的值設為nil。

runtime源碼,objc-weak.m 的 arr_clear_deallocating 函數(shù)。
Weak指針如何注冊到Weak表中、如何維護hash表可以參考objc-weak.m中的其它源碼。
從實現(xiàn)中可以看出,Weak指針的使用涉及到Hash表的增刪改查,有一定的性能開銷。

Weak指針的實際應用:
iOS 8 特有iOS相關的漏洞
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
iOS 8 的UIScrollView的delegate屬性

簡單來說,方法首先根據(jù)對象地址獲取所以Weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為nil,最后把這個entry從Weak表中刪除。

22.遠程推送

當app不在前臺時,app可能將無法接收到服務器的消息,這時蘋果通過自己的服務器來和iOS設備來通信,將最新消息傳遞到iOS設備,然后iOS系統(tǒng)將指定消息投遞給對應的app。

iOS推送通知的基本原理:
蘋果的推送服務通知是由自己專門的推送服務器APNs (Apple Push Notification service)來完成的,其過程是 APNs 接收到我們自己的應用服務器發(fā)出的被推送的消息,將這條消息推送到指定的 iOS 的設備上,然后再由 iOS設備通知到我們的應用程序,我們將會以通知或者聲音的形式收到推送回來的消息。 iOS 遠程推送的前提是,裝有我們應用程序的 iOS 設備,需要向 APNs 服務器注冊,注冊成功后,APNs 服務器將會給我們返回一個 devicetoken,我們獲取到這個 token 后會將這個 token 發(fā)送給我們自己的應用服務器。當我們需要推送消息時,我們的應用服務器將消息按照指定的格式進行打包,然后結合 iOS 設備的 devicetoken 一起發(fā)給 APNs 服務器。我們的應用會和 APNs 服務器維持一個基于 TCP 的長連接,APNs 服務器將新消息推送到iOS 設備上,然后在設備屏幕上顯示出推送的消息。

流程中包含四次交互--四次握手:
iOS設備在剛激活時將首先向APNs服務器打招呼--請求tls連接,也就是說建立一個基于tls協(xié)議的長連接(這種連接可以在保證雙發(fā)的身份可信的情況下,對連接后續(xù)的通信過程進行加密,保證通信信息的安全保密性。)
APNs服務器收到請求后,將反饋給iOS設備一個APNs服務器的證書;
iOS設備收到證書后驗證通過,將反饋給APNs服務器自己的證書和私鑰,二者都是設備激活過程中獲得并存在設備的鑰匙串中的(并非apple開發(fā)者網(wǎng)站上建立的證書)。
APNs服務器接收到后,通過發(fā)來的證書和私鑰驗證連接可靠,握手成功。

針對第三個步驟:app發(fā)送device token給自己的服務器
這一步是需要開發(fā)者自己來保證安全的一步,一般我們可以模擬蘋果的機制,對device token進行對稱加密,或者與服務器協(xié)商好撒鹽和md5的方式,保證通信過程中即使被截獲也無法被破解。當然為了提高安全級別,可以加入認證授權的操作,甚至模仿蘋果的tls方式來進一步保證安全性。

針對第四個步驟:provider發(fā)送消息給APNs服務器
這一步安全性通常有兩種方式來保證:
第一種是經(jīng)典的做法,也是與設備和APNs服務器相同的做法,即通過建立tls長連接來校驗身份,四次握手后開始發(fā)送消息。與第一步差別在于第三次握手時傳給APNs服務器的apple開發(fā)者網(wǎng)站注冊的推送證書和私鑰。這樣APNs服務器可以通過證書判斷當前通過信的對方是可信的服務器,連接建立成功,provider便可以給PNs服務器發(fā)送推送消息,要求其轉發(fā)。

針對第五個步驟:APNs服務器推送消息到設備
這一步的安全性其實是基于上面注冊推送服務時已經(jīng)建立好的基于tls協(xié)議的長連接,這時APNs服務器根據(jù)provider的請求中device token使用秘鑰解碼得到設備的id和app的bundle id, 這樣通過設備的id 判斷這個設備在線并且可以信任,直接將推送消息發(fā)給設備上的對應的app,app中可以通過api來獲取到最新一條推送的內(nèi)容:
didReceiveRemoteNotification 或 applicationdidFinishLaunchingWithOptions
第二個接口對應app已經(jīng)掛起后用戶點擊推送的消息彈窗后的響應。

但是設備如果不在線呢?
關于這種情況,官方說法是有一種QoS保障: 當設備不在線時,如果收到很多條推送,只保留最后一條推送一定時間,如果超時則舍棄。

推送的過程經(jīng)過如下步驟:
1.首先,我們的設備安裝了具有推送功能的應用(應用程序要用代碼注冊消息推動),我們的 iOS設備在有網(wǎng)絡的情況下會連接APNs推送服務器,連接過程中,APNS 服務器會驗證devicetoken,連接成功后維持一個基于TCP 的長連接;
2.Provider(我們自己的應用服務器)收到需要被推送的消息并結合被推送的 iOS設備的devicetoken一起打包發(fā)送給APNS服務器;
3.APNS服務器將推送信息推送給指定devicetoken的iOS設備;
4.iOS設備收到推送消息后通知我們的應用程序并顯示和提示用戶(聲音、彈出框)

23.熱修復

4種熱修復方式

1.Dynamic Framework
區(qū)別于靜態(tài)庫的編譯時鏈接,動態(tài)庫是在程序運行時動態(tài)加載的,并且可以多個程序共享,動態(tài)庫中包含修復的的代碼即可實現(xiàn)熱修復。
這種技術問題在于雖然apple從xcode6開放了用戶自己建立動態(tài)framework的權利(參考iOS中的SDK開發(fā)),但是為了保證系統(tǒng)安全性(可能多個程序公用同一個動態(tài)庫,存在兼容性問題),apple只允許系統(tǒng)自己的動態(tài)庫出現(xiàn),而用戶自己開發(fā)的這種動態(tài)庫只能用于企業(yè)分發(fā)的app,無法用在上架appstore的app中。

2.CodePush
CodePush是微軟推出的一種熱修復技術,但主要用戶對app中ReactNative(ReactNative是facebook提供的一種開源框架,能夠用JavaScript腳本就可以寫出App的界面,使用JS語法進行跨平臺開發(fā))部分的熱修復

3.Lua+wax
Wax 阿里開發(fā),lua腳本,支持arm64,普及少
2013年之前wax雖然有調(diào)試麻煩、線程不安全等問題,但已經(jīng)是當時不錯的熱修復方案,但是12年推出的64位的iphone5s,讓一直以來只支持32位的wax陷入尷尬,Lua字節(jié)碼也有32位與64位編譯區(qū)分,所以原來的Wax中的stdlib庫在64位無法運行,阿里從13年接手了長期無人維護的wax,修改了原有的Lua字節(jié)碼打包邏輯使其能在64位正常運行。另外,阿里完善了block調(diào)用、lua調(diào)試等方面,讓wax方案更加易用。
Lua是一個在大量的游戲中使用的簡潔、輕量、可擴展的腳本語言,用于實現(xiàn)游戲的可配置和可更新。但Lua需要預先綁定很多C函數(shù)才可在腳本中使用,所以單獨使用Lua無法做到高復用性。Wax的核心邏輯是替換函數(shù),并且連接了Lua與Objective-C runtime,使得Lua可以調(diào)用和替換任意類的方法,甚至新增類、方法。這樣一來就能在app不發(fā)布新版的情況下,通過遠程下載腳本的方式修復線上app里的bug、甚至新增一些功能。
使用時,工程需要集成Lua解釋器和Wax框架。

4.JSPatch
與Lua+wax方案的想法相同,微信團隊(bang)利用Objective-C Runtime的特性及iOS7系統(tǒng)開始支持的JavaScript腳本運行,開發(fā)了自己的JavaScript熱修復方案--JSPatch。
JSPatch中JavaScript腳本運行的基礎是JavaScriptCore,JavaScriptCore是一種JavaScript引擎,主要為webkit提供腳本處理能力,可以將JavaScript的能力更輕便地、高性能地帶給原生的iOS應用,可以實現(xiàn)oc和JavaScript兩種語言的互轉。
JSPatch能做到通過JS調(diào)用和改寫OC方法:通過類名和方法名反射得到相應的類和方法,也可以替換某個類的方法為新的實現(xiàn),或新注冊一個類,為類添加方法。JSPatch 的原理就是:JS傳遞字符串給OC,OC通過 Runtime 接口調(diào)用和替換OC方法。實際使用時,我們一般通過下發(fā)js腳本,在app啟動時判斷是否需要執(zhí)行腳本,如果有故障需要修復,就執(zhí)行指定的js腳本。如果我們自己實現(xiàn)js腳本的下發(fā),需要自己開發(fā)平臺接口,保證腳本傳輸?shù)陌踩院透卟l(fā)能力。JSPatch善解人意的提供了這樣的JSPatch平臺,并且開發(fā)了OC轉JS工具,便于將待修復的OC代碼轉為熱修復的JS代碼。

熱修復需要注意:
蘋果未來將禁用APP內(nèi)部的“動態(tài)分發(fā)”功能,蘋果禁的JSPatch / wax/ rollout熱修復框架
要有及時的線上產(chǎn)品故障報警,包含crash的報警和核心業(yè)務出錯的報警,開發(fā)及時定位到問題;
對于已經(jīng)通過熱修復解決的問題,下個版本要替換為新的native代碼;
不要使用熱修復添加業(yè)務模塊或大量非修復性的邏輯代碼,將給維護帶來很大麻煩;
熱修復對應的補丁要充分考慮相關業(yè)務影響,測試充分,避免熱修復帶來新問題;

24.組件化開發(fā)

組件化開發(fā),就是將一個臃腫,復雜的單一工程的項目, 根據(jù)功能或者屬性進行分解,拆分成為各個獨立的功能模塊或者組件 ; 然后根據(jù)項目和業(yè)務的需求,按照某種方式, 任意組織成一個擁有完整業(yè)務邏輯的工程。這就是所謂的組件化開發(fā)。

組件化開發(fā)的優(yōu)點
既然針對上述問題提到了組件化開發(fā),那就要必要交代一下組件化模塊化開發(fā)的好處。這樣在進行對比的時候,可以更加清楚的定位我們想要解決的問題。開判斷組件化開發(fā)是不是我們需要的團隊開發(fā)模式。
1、組件之間相互獨立。各組件開發(fā)成員之間的代碼想相互獨立編寫的,獨立編譯,獨立運行和獨立測試的。
2、資源的重復里用,尤其是功能性,工具性的代碼,可以很輕松的重復里用
3、迭代的效率提高。通過迭代進行功能的增減,只需要進行組件的拆分和組合。很方便也很高效

組件化開發(fā)需要注意的問題
新項目在進行組件化拆分的時候;或者老項目就行組件化重構的時候需要考慮一下幾個問題。比較對于耦合度很高的老項目,解耦并不是一件容易的事情。
1、 組件拆分的依據(jù),即要把哪些內(nèi)容劃分成為一個組件?
可以按照以下幾個方面進行拆分
① 基礎組件
全局常量、常用宏、常用的分類、常用三方框架的隔離封裝、還有一些比較常用的小功能類
② 功能組件
圖片輪播器、圖文菜單、視頻中的彈幕、相機、錄像、二維碼、下載功能、個性定制的提示框等等,都可以封裝在一個組件中
③ 業(yè)務模塊
例如電商的購物車,訂單管理、下單流程、個人中心
再例如視頻或者直播的會員管理、視頻播放全屏,右下角小屏幕,緩存等等
2、組件化存在方式
一直在說組件化,到底什么是組件呢。組件的存在方式又是什么呢?
組件形式: 每個組件都是以pod庫的形式存在
組件內(nèi)部:組件內(nèi)部按照自己喜歡的開發(fā)模式以文件夾的形式進行劃分
組件測試:每個組件對單獨對應一個demo,用來完成該組件的功能測試,這樣測試機能被解耦開
3、組件的組合方式
既然組件的存在方式是以每個pod庫的形式存在的。那么我們組合組件的方法就是通過利用CocoaPods的方式添加安裝各個組件。

25.Instruments性能檢測

Time Profiler:分析代碼的執(zhí)行時間,找出導致程序變慢的原因。

Zombies:檢查是否訪問了僵尸對象,但是這個工具只能從上往下檢查,不智能

Allocations:用來檢查內(nèi)存分配,寫算法的那批人也用這個來檢查

Leaks:檢查內(nèi)存,看是否有內(nèi)存泄露

Cocoa Layout:自動布局

Core Animation:核心動畫
FPS:一秒鐘渲染多少幀 Frame Per Second = FPS。
Core Animation給我們提供了周期性的FPS,并且考慮到了發(fā)生在程序之外的動畫,界面滑動FPS可以進行測試。一般FPS是60左右,過于低的話需要進行優(yōu)化。

26.加密

常見加密方案
編碼方案 Base64
哈希(散列)函數(shù) MD5(消息摘要算法)
SHA1
SHA256
對稱加密算法 DES
AES
非對稱加密算法 RSA
HTTPS HTTP+SSL協(xié)議

對稱加密的特點
加密/解密使用相同的密鑰
加密和解密的過程是可逆的
經(jīng)典算法
DES 數(shù)據(jù)加密標準
AES 高級加密標準
提示:
加密過程是先加密,再base64編碼
解密過程是先base64解碼,再解密

非對稱加密的特點
使用 公鑰 加密,使用 私鑰 解密
使用 私鑰 加密,使用 公鑰 解密(私鑰簽名,公鑰驗簽)
公鑰是公開的,私鑰保密
加密處理安全,但是性能極差
經(jīng)典算法-->RSA

RSA——非對稱加密,會產(chǎn)生公鑰和私鑰,公鑰在客戶端,私鑰在服務端。公鑰用于加密,私鑰用于解密。

AES——對稱加密,直接使用給定的秘鑰加密,使用給定的秘鑰解密。(加密解密使用相同的秘鑰)

MD5——一種單向的加密方式,只能加密,不能解密
對不同的數(shù)據(jù)加密,得到的結果是定長的,MD5對不同的數(shù)據(jù)進行加密,得到的結果都是 32 個字符長度的字符串

消息認證機制(HMAC)簡單說明
原理
消息的發(fā)送者和接收者有一個共享密鑰
發(fā)送者使用共享密鑰對消息加密計算得到MAC值(消息認證碼)
消息接收者使用共享密鑰對消息加密計算得到MAC值
比較兩個MAC值是否一致
使用
客戶端需要在發(fā)送的時候把(消息)+(消息·HMAC)一起發(fā)送給服務器
服務器接收到數(shù)據(jù)后,對拿到的消息用共享的KEY進行HMAC,比較是否一致,如果一致則信任

Base64編碼——對字節(jié)數(shù)組轉換成字符串的一種編碼方式
特點:可以將任意的二進制數(shù)據(jù)進行Base64編碼
結果:所有的數(shù)據(jù)都能被編碼為并只用65個字符(A~Z a~z 0~9 + / =)就能表示的文本文件。
注意:對文件進行base64編碼后文件數(shù)據(jù)的變化:編碼后的數(shù)據(jù)~=編碼前數(shù)據(jù)的4/3,會大1/3左右。

27.優(yōu)化CPU消耗

1.對象創(chuàng)建
原因:對象的創(chuàng)建會分配內(nèi)存、調(diào)整屬性,甚至還有文件讀寫等操作,比較消耗 CPU 資源。那么使用輕量級的對象,可能會對性能有所優(yōu)化。比如 CALayer 比 UIView 要輕量許多,那么不需要響應觸摸事件的對象,用 CALayer 比較合適。通過 sb 創(chuàng)建視圖對象時,其資源消耗要比直接使用代碼創(chuàng)建對象要大的多。在性能敏感的界面中,sb 不是一個好的選擇。

解決方案:
(1) 盡量推遲對象創(chuàng)建的時間,并把對象的創(chuàng)建分配到多個任務中去(推薦使用懶加載)。
(2) 如果對象可以復用,而且復用的代價比釋放、創(chuàng)建新對象要小,那么這類對象要盡量放在一個緩存池中復用。

2.對象調(diào)整
原因:對象調(diào)整也是經(jīng)常消耗 CPU 資源的地方,在這里特別說明一下 CALayer,CALayer 內(nèi)部并沒有屬性,當調(diào)用屬性方法的時候,它內(nèi)部是通過運行時為對象臨時添加一個方法,并把對應的值保存在內(nèi)部的一個字典當中,同時還會通知 delegate、創(chuàng)建動畫等,非常消耗資源。UIView 的關于顯示的相關屬性(frame/bounds/transform)等實際上都是 CALayer 的屬性映射而來的,所以對 UIView 的這些屬性進行調(diào)整的時候,消耗的資源要遠大于一般的屬性。

解決方案:當視圖層次調(diào)整的時候,消耗的資源要遠大于一般的屬性,所以在優(yōu)化性能時,應該盡量避免調(diào)整視圖層次、添加和移除視圖。

3.對象銷毀
原因:對象銷毀雖然消耗資源不多,但累計起來也是不容忽視的。通常當容器類持有大量對象時,其銷毀時的資源消耗也就相當明顯。

解決方案:對象的釋放也盡量放到后臺線程中去執(zhí)行。這里有個坑:把對象捕捉到 block 中,然后扔到后臺去隨便發(fā)個消息以避免編譯器警告,就可以讓對象在后臺銷毀了。

4.布局計算
視圖布局的計算是 App 中常見的消耗 CPU 資源的地方,如果能在后臺線程中提前計算好視圖布局,并對布局進行緩存的話,那么這個地方就不會出現(xiàn)任務性能問題了。

不論通過何種技術對視圖進行布局,最終都會落到 UIView.frame/bounds/center 等屬性的調(diào)整上,所以盡量提前計算調(diào)整好布局,在需要時一次性調(diào)整好對應屬性,而不要多次、頻繁的計算和調(diào)整這些屬性。

5.Autolayout
Autolayout 是蘋果集成的技術,在大部分情況下也可以很好的提高開發(fā)效率,但是 Autolayout 對復雜視圖來說常常會產(chǎn)生嚴重的性能問題。隨著視圖的增長, Autolayout 帶來的 CPU 消耗會呈指數(shù)級上升。如果不想手動調(diào)整 frame 等屬性,可以使用第三方 SDK。

6.文本計算
如果一個界面中包含大量的文本,文本的寬高計算會占用很大一部分資源,并且不可避免。如果沒有特殊要求,可以參考下 UILabel 內(nèi)部的實現(xiàn)方式:[NSAttributeString boundingRectWithSize:context:]來計算文本寬高,用 [NSAttributrString drawWithRect:options:context:] 來繪制文本。盡管這兩個方法性能不錯,但是扔需要放到后臺線程以避免阻塞主線程。

如果你使用 CoreText 繪制文本,那就可以先生成 CoreText 排版對象,然后自己計算,并且 CoreText 對象還可以保留以供稍后繪制使用。

7.文本渲染
屏幕上能看到的所有文本內(nèi)容控件,包括 UIWebView,在底層都是通過 CoreText 排版,繪制為 bitmap 圖顯示的。常見的文本控件(UILabel、UITextView等),其排版和繪制都是在主線程中進行的。當顯示大量文本的時候,CPU 的壓力會非常大。對此解決方案只有一個,那就是自定義文本控件,用 TextKit 或者最底層的 CoreText 對文本異步繪制。盡管這實現(xiàn)起來非常麻煩,但帶來的優(yōu)勢也非常大,CoreText 對象創(chuàng)建好以后,能直接獲取文本寬高等信息,避免了多次計算(調(diào)整 UILabel 時計算一遍,UILabel 繪制時內(nèi)部在計算一遍);CoreText 占用內(nèi)存較少,可以緩存下來以備多次渲染。

8.圖片的解碼
當你用 UIImage 或者 CGImageSource 的那幾個方法去創(chuàng)建圖片的時候,圖片數(shù)據(jù)并不會立刻解碼。圖片設置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 CPU 前,CGImage 中的數(shù)據(jù)才會得到解碼,這一步是發(fā)生在主線程的,并且不可避免。如果想繞開這個機制,常見的做法就是在后臺線程先把圖片繪制到 CGBitmapContent 中,然后從 Bitmap 直接創(chuàng)建圖片。目前常見的網(wǎng)絡圖片庫都有這個功能

9.圖片的繪制
圖片的繪制通常是指那些以 CG 開頭的方法把圖像放到畫布中,然后從畫布中創(chuàng)建圖片并顯示的一個過程,最常見的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是線程安全的,所以圖像的繪制可以很容易的放到后臺線程去執(zhí)行

28.單例

單例的意思就是一個特殊的類,而不是一個實例,單例是全局都可以使用的唯一的一個類
系統(tǒng)單例:
1、UIApplication(應用程序實例)
2、NSNotificationCenter(消息中心):
3、NSFileManager(文件管理):
4、NSUserDefaults(應用程序設置):
5、NSURLCache(請求緩存):
6、NSHTTPCookieStorage(應用程序cookies池):

1.單例模式的要點:
  顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創(chuàng)建這個實例;三是它必須自行向整個系統(tǒng)提供這個實例。
2.單例模式的優(yōu)點:
  1.安全性和唯一性:Singleton 會阻止其他對象實例化其自己的 Singleton 對象的副本,從而確保所有對象都訪問唯一實例。單例類也可以防止他人復制(copy),保留(retain)或釋放(release)實例。如果您發(fā)現(xiàn)需要,您可以創(chuàng)建自己的單例。例如,如果您有一個類為應用程序中的其他對象提供聲音,則可以將其設為單例。
  2.靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程

創(chuàng)建步驟
1、為你的單例類聲明一個靜態(tài)的實例(聲明靜態(tài)全局變量),并且初始化它的值為nil; eg:
static TestSingleton *testSingleton = nil;
這樣,在獲取實例的方法中,只有在靜態(tài)實例為nil的時候,產(chǎn)生一個你的類的實例,這個實例通常被稱為共享的實例;
2、重寫allocWithZone方法,用于確定:不能夠使用其他的方法創(chuàng)建我們類的實例,限制用戶只能通過獲取實例的方法得到這個類的實例。所以我們在allocWithZone方法中直接返回共享的類實例;
3、寫+(instancetype)shareSingleton的函數(shù)體
創(chuàng)建方法

一、傳統(tǒng)方法

+(instancetype)shareSingleton{ 
static Singleton *singleton = nil; 
if (singleton == nil){ 
singleton = [[self alloc] init]; 
} 
return singleton; 
}

二、推薦方法(GCD)

+(instancetype)shareSingleton{ 
static Singleton *singleton = nil; 
//給單例加一個線程鎖 
static dispatch_once_t onceToken; 
dispatch_once(&onceToken, ^{ 
singleton = [[Singleton alloc] init]; 
}); 
return singleton;

三、線程加鎖方法(不推薦使用)

+(instancetype)shareSingleton{ 
static Singleton *singleton = nil; 
@synchronized(self) { 
singleton = [[Singleton alloc] init]; 
} 
return singleton; 
}

注:“線程加鎖方法”這樣性能不是很好,因為每次調(diào)用+ (instancetype)sharedSingleton函數(shù)都會付出取鎖的代價
吞吞吐吐:

這樣寫的話是保證了線程安全,但通過自帶的alloc或者new來進行實例化,還是不能保證該對象只被創(chuàng)建一次,如何避免呢?他就回答不上了,其實很簡單:

.h頭文件:
@interface TestSingleton : NSObject
@property (nonatomic, copy)NSString *testStr;
+ (TestSingleton *)shareinstance;
@end
.m文件:
+ (TestSingleton *)shareinstance {
    static TestSingleton  *testSingleton = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
//testSingleton = [self singletonAlloc]; //后面使用該創(chuàng)建方法  
testSingleton = [self new];//[[self alloc]init];
    });
    return testSingleton;
}

//我們需要重載alloc、new方法

+ (instancetype)singletonAlloc
{
    return [super alloc];
}

+ (instancetype)alloc
{
//加斷言,使用alloc會蹦,并且reason提示
    NSAssert(NO, @"不要用alloc,要使用singletonAlloc方法創(chuàng)建");
    return nil;
}
+ (instancetype)new
{
    return [self alloc];
}

+ (TestSingleton *)shareinstance {
    static TestSingleton  *testSingleton = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    testSingleton = [[self singletonAlloc]init]; 
    });
    return testSingleton;
}

重載了alloc、allocWithZone和new方法,在alloc方法中加上斷言來提醒,讓用戶不能使用alloc創(chuàng)建實例。

普通類允許調(diào)用者根據(jù)需要創(chuàng)建盡可能多的類的實例,而對于單例類,每個進程只能有一個類的實例,保證了單例數(shù)據(jù)的唯一性,安全性

29.Objective-C堆和棧的區(qū)別?

答: 管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。

申請大?。?br> 棧:在Windows下,棧是向低地址擴展的數(shù)據(jù)結構,是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數(shù)),如果申請的空間超過棧的剩余空間時,將提示overflow。因 此,能從棧獲得的空間較小。

堆:堆是向高地址擴展的數(shù)據(jù)結構,是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。

碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內(nèi)存塊從棧中間彈出

分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進行釋放,無需我們手工實現(xiàn)。

分配效率:棧是機器系統(tǒng)提供的數(shù)據(jù)結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機制是很復雜的。

30.關鍵字const有什么含意?修飾類呢?static的作用,用于類呢?還有extern c的作用

答:
const 意味著"只讀",下面的聲明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作用是一樣,a是一個常整型數(shù)。
第三個意味著a是一個指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。
第四個意思a是一個指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。
最后一個意味著a是一個指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)。

結論:
關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數(shù)為常量是為了告訴了用戶這個參數(shù)的應用目的。
如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的) 通過給優(yōu)化器一些附加的信息,使用關鍵字const也許能產(chǎn)生更緊湊的代碼。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現(xiàn)。

1).欲阻止一個變量被改變,可以使用 const 關鍵字。在定義該 const 變量時,通常需要對它進行初
始化,因為以后就沒有機會再去改變它了;
2).對指針來說,可以指定指針本身為 const,也可以指定指針所指的數(shù)據(jù)為 const,或二者同時指
定為 const;
3).在一個函數(shù)聲明中,const 可以修飾形參,表明它是一個輸入?yún)?shù),在函數(shù)內(nèi)部不能改變其值;
4).對于類的成員函數(shù),若指定其為 const 類型,則表明其是一個常函數(shù),不能修改類的成員變量;
5).對于類的成員函數(shù),有時候必須指定其返回值為 const 類型,以使得其返回值不為“左值”。

31.關鍵字volatile有什么含意?并給出三個不同的例子。

答:一個定義為 volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。
下面是volatile變量的幾個例子:
并行設備的硬件寄存器(如:狀態(tài)寄存器)
一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
多線程應用中被幾個任務共享的變量

32. 一個參數(shù)既可以是const還可以是volatile嗎? 一個指針可以是volatile 嗎?解釋為什么。

答:1).是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2).是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。

33.static 關鍵字的作用:

答:
1).函數(shù)體內(nèi) static 變量的作用范圍為該函數(shù)體,不同于 auto 變量,該變量的內(nèi)存只被分配一次,
因此其值在下次調(diào)用時仍維持上次的值;
2).在模塊內(nèi)的 static 全局變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問;
3).在模塊內(nèi)的 static 函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個函數(shù)的使用范圍被限制在聲明
它的模塊內(nèi);
4).在類中的 static 成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
5).在類中的 static 成員函數(shù)屬于整個類所擁有,這個函數(shù)不接收 this 指針,因而只能訪問類的static 成員變量。

34.線程與進程的區(qū)別和聯(lián)系?

答:
1). 進程和線程都是由操作系統(tǒng)所體會的程序運行的基本單元,系統(tǒng)利用該基本單元實現(xiàn)系統(tǒng)對應用的并發(fā)性
2). 進程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。
3). 進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產(chǎn)生影響,而線程只是一個進程中的不同執(zhí)行路徑。
4.)線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉。所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。
5). 但對于一些要求同時進行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進程。

35.列舉幾種進程的同步機制,并比較其優(yōu)缺點。

答: 原子操作 信號量機制 自旋鎖 管程,會合,分布式系統(tǒng)

36. 進程之間通信的途徑

答:共享存儲系統(tǒng)消息傳遞系統(tǒng)管道:以文件系統(tǒng)為基礎

37. 進程死鎖的原因

答:資源競爭及進程推進順序非法

38. 死鎖的4個必要條件

答:互斥、請求保持、不可剝奪、環(huán)路

39. 死鎖的處理

答:鴕鳥策略、預防策略、避免策略、檢測與解除死鎖

40.cocoa touch框架

答:iPhone OS 應用程序的基礎 Cocoa Touch 框架重用了許多 Mac 系統(tǒng)的成熟模式,但是它更多地專注于觸摸的接口和優(yōu)化。

UIKit 為您提供了在 iPhone OS 上實現(xiàn)圖形,事件驅動程序的基本工具,其建立在和 Mac OS X 中一樣的 Foundation 框架上,包括文件處理,網(wǎng)絡,字符串操作等。

Cocoa Touch 具有和 iPhone 用戶接口一致的特殊設計。有了 UIKit,您可以使用 iPhone OS 上的獨特的圖形接口控件,按鈕,以及全屏視圖的功能,您還可以使用加速儀和多點觸摸手勢來控制您的應用。

各色俱全的框架 除了UIKit 外,Cocoa Touch 包含了創(chuàng)建世界一流 iPhone 應用程序需要的所有框架,從三維圖形,到專業(yè)音效,甚至提供設備訪問 API 以控制攝像頭,或通過 GPS 獲知當前位置。

Cocoa Touch 既包含只需要幾行代碼就可以完成全部任務的強大的 Objective-C 框架,也在需要時提供基礎的 C 語言 API 來直接訪問系統(tǒng)。這些框架包括:
Core Animation:通過 Core Animation,您就可以通過一個基于組合獨立圖層的簡單的編程模型來創(chuàng)建豐富的用戶體驗。
Core Audio:Core Audio 是播放,處理和錄制音頻的專業(yè)技術,能夠輕松為您的應用程序添加強大的音頻功能。
Core Data:提供了一個面向對象的數(shù)據(jù)管理解決方案,它易于使用和理解,甚至可處理任何應用或大或小的數(shù)據(jù)模型。

下面是 Cocoa Touch 中一小部分可用的框架:
音頻和視頻:Core Audio ,OpenAL ,Media Library ,AV Foundation
數(shù)據(jù)管理 :Core Data ,SQLite
圖形和動畫 :Core Animation ,OpenGL ES ,Quartz 2D
網(wǎng)絡:Bonjour ,WebKit ,BSD Sockets
用戶應用:Address Book ,Core Location ,Map Kit ,Store Kit

41.自動釋放池是什么,如何工作

答:當您向一個對象發(fā)送一個autorelease消息時,Cocoa就會將該對象的一個引用放入到最新的自動釋放.它仍然是個正當?shù)膶ο螅虼俗詣俞尫懦囟x的作用域內(nèi)的其它對象可以向它發(fā)送消息。當程序執(zhí)行到作用域結束的位置時,自動釋放池就會被釋放,池中的所有對象也就被釋放。

42. Objective-C的優(yōu)缺點。

答:objc優(yōu)點:
1). Cateogies
2). Posing
3). 動態(tài)識別
4).指標計算
5).彈性訊息傳遞
6).不是一個過度復雜的 C 衍生語言
7).Objective-C 與 C++ 可混合編程
objc缺點:
1).不支援命名空間
2).不支持運算符重載
3).不支持多重繼承
4).使用動態(tài)運行時類型,所有的方法都是函數(shù)調(diào)用,所以很多編譯時優(yōu)化方法都用不到。(如內(nèi)聯(lián)函數(shù)等),性能低劣。

43.sprintf,strcpy,memcpy使用上有什么要注意的地方。

答:
1). sprintf是格式化函數(shù)。將一段數(shù)據(jù)通過特定的格式,格式化到一個字符串緩沖區(qū)中去。sprintf格式化的函數(shù)的長度不可控,有可能格式化后的字符串會超出緩沖區(qū)的大小,造成溢出。
2).strcpy是一個字符串拷貝的函數(shù),它的函數(shù)原型為strcpy(char *dst, const char *src
將src開始的一段字符串拷貝到dst開始的內(nèi)存中去,結束的標志符號為 ‘\0',由于拷貝的長度不是由我們自己控制的,所以這個字符串拷貝很容易出錯。
3). memcpy是具備字符串拷貝功能的函數(shù),這是一個內(nèi)存拷貝函數(shù),它的函數(shù)原型為memcpy(char dst, const char src, unsigned int len);將長度為len的一段內(nèi)存,從src拷貝到dst中去,這個函數(shù)的長度可控。但是會有內(nèi)存疊加的問題。

44.http和scoket通信的區(qū)別。

答:HTTP超文本傳輸協(xié)議,是短連接,是客戶端主動發(fā)送請求,服務器做出響應,服務器響應之后,鏈接斷開。HTTP是一個屬于應用層面向對象的協(xié)議,HTTP有兩類報文:請求報文和響應報文。
HTTP請求報文:一個HTTP請求報文由請求行、請求頭部、空行和請求數(shù)據(jù)4部分組成。
HTTP響應報文:由三部分組成:狀態(tài)行、消息報頭、響應正文。
http是客戶端用http協(xié)議進行請求,發(fā)送請求時候需要封裝http請求頭,并綁定請求的數(shù)據(jù),服務器一般有web服務器配合(當然也非絕對)。 http請求方式為客戶端主動發(fā)起請求,服務器才能給響應,一次請求完畢后則斷開連接,以節(jié)省資源。服務器不能主動給客戶端響應(除非采取http長連接 技術)。iphone主要使用類是NSUrlConnection。

scoket是客戶端跟服務器直接使用socket“套接字”進行連接,并沒有規(guī)定連接后斷開,所以客戶端和服務器可以保持連接通道,雙方 都可以主動發(fā)送數(shù)據(jù)。一般在游戲開發(fā)或股票開發(fā)這種要求即時性很強并且保持發(fā)送數(shù)據(jù)量比較大的場合使用。主要使用類是CFSocketRef。

45.TCP和UDP的區(qū)別

答: TCP全稱是Transmission Control Protocol,中文名為傳輸控制協(xié)議,它可以提供可靠的、面向連接的網(wǎng)絡數(shù)據(jù)傳遞服務。傳輸控制協(xié)議主要包含下列任務和功能:

  • 確保IP數(shù)據(jù)報的成功傳遞。
  • 對程序發(fā)送的大塊數(shù)據(jù)進行分段和重組。
  • 確保正確排序及按順序傳遞分段的數(shù)據(jù)。
  • 通過計算校驗和,進行傳輸數(shù)據(jù)的完整性檢查。
    TCP連接的三次握手:
    第一次握手:客戶端發(fā)送syn包(syn=j)到服務器,并進入SYN_SEND狀態(tài),等待服務器確認;
    第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發(fā)送一個SYN包,即SYN+ACK包,此時服務器進入SYN+RECV狀態(tài);
    第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發(fā)送確認包ACK(ack=k+1),此發(fā)送完畢,客戶端和服務器進入ESTABLISHED狀態(tài),完成三次狀態(tài)。
    UDP為用戶數(shù)據(jù)報協(xié)議,非連接的不可靠的點到多點的通信;
    TCP提供的是面向連接的、可靠的數(shù)據(jù)流傳輸,而UDP提供的是非面向連接的、不可靠的數(shù)據(jù)流傳輸。
    簡單的說,TCP注重數(shù)據(jù)安全,而UDP數(shù)據(jù)傳輸快點,但安全性一般

46.請簡要說明viewDidLoad和viewDidUnload何時調(diào)用

答:viewDidLoad在view從nib文件初始化時調(diào)用,loadView在controller的view為nil時調(diào)用。此方法在編程實現(xiàn)view時調(diào)用,view控制器默認會注冊memory warning notification,當view controller的任何view沒有用的時候,viewDidUnload會被調(diào)用,在這里實現(xiàn)將retain的view release,如果是retain的IBOutlet view 屬性則不要在這里release,IBOutlet會負責release 。

簡述視圖控件器的生命周期。
loadView 盡管不直接調(diào)用該方法,如多手動創(chuàng)建自己的視圖,那么應該覆蓋這個方法并將它們賦值給試圖控制器的 view 屬性。

viewDidLoad 只有在視圖控制器將其視圖載入到內(nèi)存之后才調(diào)用該方法,這是執(zhí)行任何其他初始化操作的入口。

viewDidUnload 當試圖控制器從內(nèi)存釋放自己的方法的時候調(diào)用,用于清楚那些可能已經(jīng)在試圖控制器中創(chuàng)建的對象。

viewVillAppear 當試圖將要添加到窗口中并且還不可見的時候或者上層視圖移出圖層后本視圖變成頂級視圖時調(diào)用該方法,用于執(zhí)行諸如改變視圖方向等的操作。實現(xiàn)該方法時確保調(diào)用 [super viewWillAppear:

viewDidAppear 當視圖添加到窗口中以后或者上層視圖移出圖層后本視圖變成頂級視圖時調(diào)用,用于放置那些需要在視圖顯示后執(zhí)行的代碼。確保調(diào)用 [super viewDidAppear:] 。

47. 簡述內(nèi)存分區(qū)情況

答:
1).代碼區(qū):存放函數(shù)二進制代碼
2).數(shù)據(jù)區(qū):系統(tǒng)運行時申請內(nèi)存并初始化,系統(tǒng)退出時由系統(tǒng)釋放。存放全局變量、靜態(tài)變量、常量
3).堆區(qū):通過malloc等函數(shù)或new等操作符動態(tài)申請得到,需程序員手動申請和釋放
4).棧區(qū):函數(shù)模塊內(nèi)申請,函數(shù)結束時由系統(tǒng)自動釋放。存放局部變量、函數(shù)參數(shù)

48.隊列和棧有什么區(qū)別:

答:隊列和棧是兩種不同的數(shù)據(jù)容器。從”數(shù)據(jù)結構”的角度看,它們都是線性結構,即數(shù)據(jù)元素之間的關系相同。
隊列是一種先進先出的數(shù)據(jù)結構,它在兩端進行操作,一端進行入隊列操作,一端進行出列隊操作。
棧是一種先進后出的數(shù)據(jù)結構,它只能在棧頂進行操作,入棧和出棧都在棧頂操作。

49.HTTP協(xié)議中,POST和GET的區(qū)別是什么?

答:GET請求:參數(shù)在地址后拼接,沒有請求數(shù)據(jù),不安全(因為所有參數(shù)都拼接在地址后面),不適合傳輸大量數(shù)據(jù)(長度有限制,為1024個字節(jié))。
GET提交、請求的數(shù)據(jù)會附在URL之后,即把數(shù)據(jù)放置在HTTP協(xié)議頭<requestline>中。以?分割URL和傳輸數(shù)據(jù),多個參數(shù)用&連接。如果數(shù)據(jù)是英文字母或數(shù)字,原樣發(fā)送,
如果是空格,轉換為+,如果是中文/其他字符,則直接把字符串用BASE64加密。
POST請求:參數(shù)在請求數(shù)據(jù)區(qū)放著,相對GET請求更安全,并且數(shù)據(jù)大小沒有限制。把提交的數(shù)據(jù)放置在HTTP包的包體<request-body>中.
GET提交的數(shù)據(jù)會在地址欄顯示出來,而POST提交,地址欄不會改變。
傳輸數(shù)據(jù)的大?。?br> GET提交時,傳輸數(shù)據(jù)就會受到URL長度限制,POST由于不是通過URL傳值,理論上書不受限。
安全性:
POST的安全性要比GET的安全性高;
通過GET提交數(shù)據(jù),用戶名和密碼將明文出現(xiàn)在URL上,比如登陸界面有可能被瀏覽器緩存。
1).GET 方法
GET 方法提交數(shù)據(jù)不安全,數(shù)據(jù)置于請求行,客戶端地址欄可見;
GET 方法不可以設置書簽
2).POST 方法
POST 方法提交數(shù)據(jù)安全,數(shù)據(jù)置于消息主體內(nèi),客戶端不可見
POST 方法可以設置書簽

50.iOS的系統(tǒng)架構

答: iOS的系統(tǒng)架構分為( 核心操作系統(tǒng)層 theCore OS layer )、( 核心服務層theCore Services layer )、( 媒體層 theMedia layer )和( Cocoa 界面服務層 the Cocoa Touch layer )四個層次。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結構(3).初始化時...
    歐辰_OSR閱讀 30,282評論 8 265
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,324評論 25 708
  • 明天是母親節(jié),我的身份今天都過了一遍! 早上得知千里之外的兒子要趕回南寧,開心的勁爆棚了! 中午從杭州...
    思慕許姐閱讀 302評論 0 0
  • 昨晚回家,半路上接到老婆打來的電話說有沒有回家了?我本以為是要等我來洗眼鏡,后來老婆說你懂的,我就知道...
    藍海煮雨閱讀 612評論 7 5
  • 通過對個案咨詢課程的學習,我對個案咨詢有了一些初步的理解。 首先,咨詢的意思是通過某些人頭腦中所儲備的知識...
    5aae4a31c9ea閱讀 1,260評論 0 0

友情鏈接更多精彩內(nèi)容