面試中被面試官問到的問題答案(一)

秦小風發表於2017-12-21

以下問題的答案是之前寫的一篇文章 面試中被面試官問到的問題 現在把問題的答案整理了一份出來給大家。希望對大家有所幫助。如果整理的答案有問題,請聯絡我。shavekevin@gmail.com

1.請你談談static和巨集定義的區別。什麼時候用static什麼時候用巨集定義。 讓你宣告的常量只在你宣告的檔案裡有作用要不編譯器會儲存

巨集定義:

1). 一般來說我們使用巨集定義最常見的是定義一些常量 簡單的”函式”(比如求兩個數的最大小值) 例如:定義常量PI


#define PI 3.1415926
複製程式碼

定義函式


#define MIN(A,B) ((A) < (B) ? (A):(B))
複製程式碼

我們不對巨集定義進行修改

  1. . 使用巨集定義可以在很大程度上可以簡化我們的程式碼 例如:我們在寫單例的時候 之前我們寫的是
#import "ShareSingleton.h"

@implementation ShareSingleton

+(instancetype)shareSingleton {

    static ShareSingleton *leader = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        leader = [[ShareSingleton alloc]init];
    });
    return leader;
}
@end
複製程式碼

//如果我們使用巨集定義的話我們可以這樣寫:

#define DISPATCH_ONCE_BLOCK(onceBlock) static dispatch_once_t onceToken; dispatch_once(&onceToken, onceBlock);
#import "ShareSingleton.h"
@implementation ShareSingleton

+(instancetype)shareSingleton {

    static ShareSingleton *leader = nil;
    DISPATCH_ONCE_BLOCK(^{
        leader = [[ShareSingleton alloc]init];
    })
    return leader;
}
@end
複製程式碼

其實#define的原理就是不管三七二十一,直接做替換,所以我們完全可以利用這個特點,發揮自己的想象,簡化程式碼~ 巨集定義實質是一個預編譯指令,在程式未執行之前將某些指令付給相應的變數。

小結一下: static標記的變數會儲存到全域性變數區,生命週期和程式相同。而巨集定義所定義的生命週期與所在的載體的生命週期有關.

static只在宣告的類中可見。 在宣告的類中結束後,再次使用還是之前的值。

2.你是怎麼看待代理 通知的 他們有什麼區別? 首先,我們把代理通知 放到一起來討論第一反映是傳值。 ok,下面慢慢來說各個的用法和區別。

通知中心

通過NSNotification可以給多個物件傳遞資料和訊息(多個傳遞) 代理 通過protocol(代理模式)只能給一個物件傳遞資料和訊息(單一傳遞)“一對一”,對同一個協議,一個物件只能設定一個代理delegate,所以單例物件就不能用代理(可以用多點回撥,下面見解)。

代理更注重過程資訊的傳輸:比如發起一個網路請求,可能想要知道此時請求是否已經開始、是否收到了資料、資料是否已經接受完成、資料接收失敗。

區別: 代理和通知的區別應該主要是一對一和一對多的關係。delegate的多點回撥相對notification更加便捷,更多方便,讓專案更好維護。

3.說說你對記憶體管理的理解。

記憶體管理原則

引用計數的增加和減少相等,當引用計數降為0之後,不應該再使用這塊記憶體空間。 凡是用alloc retain 或者copy讓記憶體的引用計數增加了。就需要使用release或者autorelease讓記憶體的引用 計數減少。在一段程式碼內。增加和減少的次數要相等。

autoreleasepool的使用

通過autoreleasepool控制autorelease物件的釋放 向一個物件傳送autorelease訊息。這個物件何時釋放取決於autoreleasepool

copy方法 跟retain不同,一個物件想要copy,生成自己的副本,需要實現NSCopying協議,定義copy的細節(如何copy)如果類沒有接受NSCoping協議而給類傳送copy訊息,會引起crash 總結: OC藉助引用計數機制去管理記憶體,凡是使用了alloc copy retain 等 方法,增加了引用計數,就要使用release 和autorelease 減少引用計數,引用計數為0的時候,物件所佔的記憶體,被系統回收。

autorelease是未來某個時間(出autorelease)引用減一,不是即時的。

不是任何物件都可以接受copy訊息。只有接受了NSCoping協議的物件才接受copy訊息。

4.談談你對iOS效能優化的理解.

談起iOS的效能優化我們首先想到的是應該是tableview表檢視的優化。關於表檢視的優化我們可以從以下幾個方面來看:

1).tableviewcell渲染 繪製時要儘可能的避免分配資源,比如UIFont,NSDateFormatter或者任何在繪製時 需要的物件,推薦使用類層級的初始化方法中執行分配,並將其儲存為靜態變數。

2).圖層渲染的問題 透明圖層對渲染效能會有一定的影響,系統必須將透明圖層與下面的檢視混合起來計算顏色,並 繪製出來。減少透明圖層並使用不透明的圖層來替代它們,可以極大地提高渲染速度。

3).為代理方法瘦身 我們要儘量避免在tableview的cellforrowatindexpath的代理方法裡寫那麼多程式碼,這樣做不僅可以簡化程式碼方便維護和管理,這對程式的執行也有幫助。

4).複雜檢視儘量採用純程式碼的方式

當 UITableViewCell擁有多個子檢視時,IOS的渲染機制會拖慢速度。重寫drawRect直接繪製內容的方式可 以提高效能,而不是在類初始化的時候初始化一些label或者imageview等。

(以下來源於yykit作者ibireme這是源連結

下面就是些CPU 資源消耗原因和解決方案 還有GPU資源消耗原因和解決方案

物件的建立會分配記憶體、調整屬性、甚至還有讀取檔案等操作,比較消耗 CPU 資源。儘量用輕量的物件代替重量的物件,可以對效能有所優化。比如 CALayer 比 UIView 要輕量許多,那麼不需要響應觸控事件的控制元件,用 CALayer 顯示會更加合適。如果物件不涉及 UI 操作,則儘量放到後臺執行緒去建立,但可惜的是包含有 CALayer 的控制元件,都只能在主執行緒建立和操作。通過 Storyboard 建立檢視物件時,其資源消耗會比直接通過程式碼建立物件要大非常多,在效能敏感的介面裡,Storyboard 並不是一個好的技術選擇。

1).物件的建立 儘量推遲物件建立的時間,並把物件的建立分散到多個任務中去。儘管這實現起來比較麻煩,並且帶來的優勢並不多,但如果有能力做,還是要儘量嘗試一下。如果物件可以複用,並且複用的代價比釋放、建立新物件要小,那麼這類物件應當儘量放到一個快取池裡複用。

2).物件調整 物件的調整也經常是消耗 CPU 資源的地方。這裡特別說一下 CALayer:CALayer 內部並沒有屬性,當呼叫屬性方法時,它內部是通過執行時 resolveInstanceMethod 為物件臨時新增一個方法,並把對應屬性值儲存到內部的一個 Dictionary 裡,同時還會通知 delegate、建立動畫等等,非常消耗資源。UIView 的關於顯示相關的屬性(比如 frame/bounds/transform)等實際上都是 CALayer 屬性對映來的,所以對 UIView 的這些屬性進行調整時,消耗的資源要遠大於一般的屬性。對此你在應用中,應該儘量減少不必要的屬性修改。 當檢視層次調整時,UIView、CALayer 之間會出現很多方法呼叫與通知,所以在優化效能時,應該儘量避免調整檢視層次、新增和移除檢視。 3). 物件銷燬 物件的銷燬雖然消耗資源不多,但累積起來也是不容忽視的。通常當容器類持有大量物件時,其銷燬時的資源消耗就非常明顯。同樣的,如果物件可以放到後臺執行緒去釋放,那就挪到後臺執行緒去。這裡有個小 Tip:把物件捕獲到 block 中,然後扔到後臺佇列去隨便傳送個訊息以避免編譯器警告,就可以讓物件在後臺執行緒銷燬了。 例如:

NSArray *tmp = self.array;
self.array = nil;
dispatch_async(queue, ^{
    [tmp class];
});
複製程式碼

4).一些計算

檢視佈局的計算是 App 中最為常見的消耗 CPU 資源的地方。如果能在後臺執行緒提前計算好檢視佈局、並且對檢視佈局進行快取,那麼這個地方基本就不會產生效能問題了。 不論通過何種技術對檢視進行佈局,其最終都會落到對 UIView.frame/bounds/center 等屬性的調整上。上面也說過,對這些屬性的調整非常消耗資源,所以儘量提前計算好佈局,在需要時一次性調整好對應屬性,而不要多次、頻繁的計算和調整這些屬性。 Autolayout 是蘋果本身提倡的技術,在大部分情況下也能很好的提升開發效率,但是 Autolayout 對於複雜檢視來說常常會產生嚴重的效能問題。隨著檢視數量的增長,Autolayout 帶來的 CPU 消耗會呈指數級上升。具體資料可以看這個文章:http://pilky.me/36/。 如果你不想手動調整 frame 等屬性,你可以用一些工具方法替代(比如常見的 left/right/top/bottom/width/height 快捷屬性),或者使用 ComponentKit、AsyncDisplayKit 等框架.

如果一個介面中包含大量文字(比如微博微信朋友圈等),文字的寬高計算會佔用很大一部分資源,並且不可避免。如果你對文字顯示沒有特殊要求,可以參考下 UILabel 內部的實現方式:用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文字寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪製文字。儘管這兩個方法效能不錯,但仍舊需要放到後臺執行緒進行以避免阻塞主執行緒。

5).文字的繪製

如果你用 CoreText 繪製文字,那就可以先生成 CoreText 排版物件,然後自己計算了,並且 CoreText 物件還能保留以供稍後繪製使用。 螢幕上能看到的所有文字內容控制元件,包括 UIWebView,在底層都是通過 CoreText 排版、繪製為 Bitmap 顯示的。常見的文字控制元件 (UILabel、UITextView 等),其排版和繪製都是在主執行緒進行的,當顯示大量文字時,CPU 的壓力會非常大。對此解決方案只有一個,那就是自定義文字控制元件,用 TextKit 或最底層的 CoreText 對文字非同步繪製。儘管這實現起來非常麻煩,但其帶來的優勢也非常大,CoreText 物件建立好後,能直接獲取文字的寬高等資訊,避免了多次計算(調整 UILabel 大小時算一遍、UILabel 繪製時內部再算一遍);CoreText 物件佔用記憶體較少,可以快取下來以備稍後多次渲染.

6).圖片的解碼

當你用 UIImage 或 CGImageSource 的那幾個方法建立圖片時,圖片資料並不會立刻解碼。圖片設定到 UIImageView 或者 CALayer.contents 中去,並且 CALayer 被提交到 GPU 前,CGImage 中的資料才會得到解碼。這一步是發生在主執行緒的,並且不可避免。如果想要繞開這個機制,常見的做法是在後臺執行緒先把圖片繪製到 CGBitmapContext 中,然後從 Bitmap 直接建立圖片。目前常見的網路圖片庫都自帶這個功能。

7).影像的繪製

影像的繪製通常是指用那些以 CG 開頭的方法把影像繪製到畫布中,然後從畫布建立圖片並顯示這樣一個過程。這個最常見的地方就是 [UIView drawRect:] 裡面了。由於 CoreGraphic 方法通常都是執行緒安全的,所以影像的繪製可以很容易的放到後臺執行緒進行。一個簡單非同步繪製的過程大致如下(實際情況會比這個複雜得多,但原理基本一致)


dispatch_async(backgroundQueue, ^{
        CGContextRef ctx = CGBitmapContextCreate(...);
        // draw in context...
        CGImageRef img = CGBitmapContextCreateImage(ctx);
        CFRelease(ctx);
        dispatch_async(mainQueue, ^{
            layer.contents = img;
        });
    });
    
複製程式碼

GPU 資源消耗原因和解決方案

1.紋理的渲染

所有的 Bitmap,包括圖片、文字、柵格化的內容,最終都要由記憶體提交到視訊記憶體,繫結為 GPU Texture。不論是提交到視訊記憶體的過程,還是 GPU 調整和渲染 Texture 的過程,都要消耗不少 GPU 資源。當在較短時間顯示大量圖片時(比如 TableView 存在非常多的圖片並且快速滑動時),CPU 佔用率很低,GPU 佔用非常高,介面仍然會掉幀。避免這種情況的方法只能是儘量減少在短時間內大量圖片的顯示,儘可能將多張圖片合成為一張進行顯示。 當圖片過大,超過 GPU 的最大紋理尺寸時,圖片需要先由 CPU 進行預處理,這對 CPU 和 GPU 都會帶來額外的資源消耗。目前來說,iPhone 4S 以上機型,紋理尺寸上限都是 4096x4096,更詳細的資料可以看這裡:iosres.com。所以,儘量不要讓圖片和檢視的大小超過這個值。

2.檢視的混合 當多個檢視(或者說 CALayer)重疊在一起顯示時,GPU 會首先把他們混合到一起。如果檢視結構過於複雜,混合的過程也會消耗很多 GPU 資源。為了減輕這種情況的 GPU 消耗,應用應當儘量減少檢視數量和層次,並在不透明的檢視裡標明 opaque 屬性以避免無用的 Alpha 通道合成。當然,這也可以用上面的方法,把多個檢視預先渲染為一張圖片來顯示

3.影像的生成

CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的向量圖形顯示,通常會觸發離屏渲染(offscreen rendering),而離屏渲染通常發生在 GPU 中。當一個列表檢視中出現大量圓角的 CALayer,並且快速滑動時,可以觀察到 GPU 資源已經佔滿,而 CPU 資源消耗很少。這時介面仍然能正常滑動,但平均幀數會降到很低。為了避免這種情況,可以嘗試開啟 CALayer.shouldRasterize 屬性,但這會把原本離屏渲染的操作轉嫁到 CPU 上去。對於只需要圓角的某些場合,也可以用一張已經繪製好的圓角圖片覆蓋到原本檢視上面來模擬相同的視覺效果。最徹底的解決辦法,就是把需要顯示的圖形在後臺執行緒繪製為圖片,避免使用圓角、陰影、遮罩等屬性。

如何檢測應用的流暢度?
複製程式碼

“過早的優化是萬惡之源”,在需求未定,效能問題不明顯時,沒必要嘗試做優化,而要儘量正確的實現功能。做效能優化時,也最好是走修改程式碼 -> Profile -> 修改程式碼這樣一個流程,優先解決最值得優化的地方。 如果你需要一個明確的 FPS 指示器,可以嘗試一下 KMCGeigerCounter。對於 CPU 的卡頓,它可以通過內建的 CADisplayLink 檢測出來;對於 GPU 帶來的卡頓,它用了一個 1x1 的 SKView 來進行監視。這個專案有兩個小問題:SKView 雖然能監視到 GPU 的卡頓,但引入 SKView 本身就會對 CPU/GPU 帶來額外的一點的資源消耗;這個專案在 iOS 9 下有一些相容問題,需要稍作調整。

5.你用過單元測試嗎?怎麼才能做好單元測試?

什麼是單元測試?

單元測試:以下內容來自維基百科單元測試

在計算機程式設計中,單元測試(英語:Unit Testing)又稱為模組測試, 是針對程式模組(軟體設計的最小單位)來進行正確性檢驗的測試工作。程式單元是應用的最小可測試部件。在過程化程式設計中,一個單元就是單個程式、函式、過程等;對於物件導向程式設計,最小單元就是方法,包括基類(超類)、抽象類、或者派生類(子類)中的方法。

通常來說,程式設計師每修改一次程式就會進行最少一次單元測試,在編寫程式的過程中前後很可能要進行多次單元測試,以證實程式達到軟體規格書要求的工作目標,沒有程式錯誤;雖然單元測試不是什麼必須的,但也不壞,這牽涉到專案管理的政策決定。

每個理想的測試案例獨立於其它案例;為測試時隔離模組,經常使用stubs、mock[1]或fake等測試馬甲程式。單元測試通常由軟體開發人員編寫,用於確保他們所寫的程式碼符合軟體需求和遵循開發目標。它的實施方式可以是非常手動的(通過紙筆),或者是做成構建自動化的一部分。
複製程式碼

單元測試有什麼好處?

單元測試的一個好處就是我們可以只測試單個模組,我們可以測試 單一模組有沒有問題。比如說我們在開發中經常會寫一些測試性的demo。我們寫的測試性demo執行正常達到了我們需要的效果,那麼我們就可以把demo的效果運用到我們的工程中進行除錯。

  • 適應變更

單元測試允許程式設計師在未來重構程式碼,並且確保模組依然工作正確(複合測試)。這個過程就是為所有函式和方法編寫單元測試,一旦變更導致錯誤發生,藉助於單元測試可以快速定位並修復錯誤。

可讀性強的單元測試可以使程式設計師方便地檢查程式碼片斷是否依然正常工作。良好設計的單元測試案例覆蓋程式單元分支和迴圈條件的所有路徑。

在連續的單元測試環境,通過其固有的持續維護工作,單元測試可以延續用於準確反映當任何變更發生時可執行程式和程式碼的表現。藉助於上述開發實踐和單元測試的覆蓋,可以分分秒秒維持準確性。

  • 簡化整合

單元測試消除程式單元的不可靠,採用自底向上的測試路徑。通過先測試程式部件再測試部件組裝,使整合測試變得更加簡單。

業界對於人工整合測試的必要性存在較大爭議。儘管精心設計的單元測試體系看上去實現了整合測試,因為整合測試需要人為評估一些人為因素才能證實的方面,單元測試替代整合測試不可信。一些人認為在足夠的自動化測試系統的條件下,人力整合測試組不再是必需的。事實上,真實的需求最終取決於開發產品的特點和使用目標。另外,人工或手動測試很大程度上依賴於組織的可用資源。[來源請求]

  • 文件記錄

單元測試提供了系統的一種文件記錄。藉助於檢視單元測試提供的功能和單元測試中如何使用程式單元,開發人員可以直觀的理解程式單元的基礎API。

單元測試具體表現了程式單元成功的關鍵特點。這些特點可以指出正確使用和非正確使用程式單元,也能指出需要捕獲的程式單元的負面表現(譯註:異常和錯誤)。儘管很多軟體開發環境不僅依賴於程式碼做為產品文件,在單元測試中和單元測試本身確實文件化了程式單元的上述關鍵特點。

另一方面,傳統文件易受程式本身實現的影響,並且時效性難以保證(如設計變更、功能擴充套件等在不太嚴格時經常不能保持文件同步更新)。

  • 表達設計

    在測試驅動開發的軟體實踐中,單元測試可以取代正式的設計。每一個單元測試案例均可以視為一項類、方法和待觀察行為等設計元素。下面的Java例可以幫助說明這一點。 當然,單元測試缺乏圖的可讀性,但UML圖可以在自由工具(通常可從IDE擴充套件獲取)中為大多數現代程式語言生成UML圖,很難要求採購昂貴的UML設計套裝軟體。自由工具,類似於基於xUnit框架的工具,測試結果輸出到一些可生成供人工識讀的圖形化工具系統中去。

  • 分離介面和實現

因為很多類會引用其它類,對這個類的測試經常會要求測試其它的類。一個最普遍的例子是依賴於資料庫的類:為了測試它,測試人員通常編寫程式碼去運算元據庫。這是不對的,因為單元測試不應超出待測試的類邊界。

作為替代,軟體開發人員應建立一個資料庫連線的抽象介面,然後實現這個介面的模擬物件。通過對程式碼所需附件的抽象(臨時降低了網狀的耦合效應),這些獨立程式單元較前者更能被完整測試。高質量的程式碼單元也可提供更好的可維護性。
複製程式碼
  • 侷限

測試不可能發現所有的程式錯誤,單元測試也不例外。按定義,單元測試只測試程式單元自身的功能。因此,它不能發現整合錯誤、效能問題、或者其他系統級別的問題。單元測試結合其他軟體測試活動更為有效。與其它形式的軟體測試類似,單元測試只能表明測到的問題,不能表明不存在未測試到的錯誤。

軟體測試是一個組合問題。例如,每一個布林型的決斷語句需要至少兩種測試:一個返回真,一個返回假。因此,針對每行書寫的程式碼,程式設計師通常需要寫3至5行的測試程式碼。[3]這很明顯地很花時間而且對此的投入可能並不值得。也有些問題是根本不能簡單地檢測出來的——例如具不確定性的或牽扯到多執行緒的問題。此外,替單元測試寫的程式碼可能就像要測試的程式碼一樣有程式錯誤。佛瑞德·布魯克斯在人月神話一書中舉例說明:“絕對不要帶兩個計時器去海邊。最好總是帶一或三個”。意味著,如果兩個計時器互相沖突的話,你該怎麼知道哪個是對的?為了獲得單元測試的好處,在軟體開發過程中應形成一套嚴格紀律意識。仔細保留記錄是必要的,不僅僅只保留執行的測試,也包括保留對應的原始碼和其它軟體單元的變更歷史。即,使用版本控制系統是必要的。如果後續版本不能通過一個以前測試通過的單元測試,版本控制系統可以提供對應時間段對原始碼所做的變更清單。

每天養成檢視單元測試案例失敗測試並及時確定錯誤原因的習慣是必要的。如果沒有這樣的流程,沒有在團隊工作流程中體現,單元測試系列將走向不同步,造成越來越多的錯誤和越來越低效的單元測試案例系列。
複製程式碼

iOS中的單元測試

   在開發中,經常用到的單元測試一是測試某個模組的功能,也就是說把這個模組獨立起來,單獨進行測試。用到最多的應該是測試模組功能和介面除錯功能。當然單元測試還有一些高階的用法自動測試和自動釋出等。

   OCUnit(即用XCTest進行測試)其實就是蘋果自帶的測試框架,我們主要講的就是這個。GHUnit是一個視覺化的測試框架。(有了它,你可以點選APP來決定測試哪個方法,並且可以點選檢視測試結果等。)OCMock就是模擬某個方法或者屬性的返回值,你可能會疑惑為什麼要這樣做?使用用模型生成的模型物件,再傳進去不就可以了?答案是可以的,但是有特殊的情況。比如你測試的是方法A,方法A裡面呼叫到了方法B,而且方法B是有引數傳入,但又不是方法A所提供。這時候,你可以使用OCMock來模擬方法B返回的值。(在不影響測試的情況下,就可以這樣去模擬。)除了這些,在沒有網路的情況下,也可以通過OCMock模擬返回的資料。UITests就是通過程式碼化來實現自動點選介面,輸入文字等功能。靠人工操作的方式來覆蓋所有測試用例是非常困難的,尤其是加入新功能以後,舊的功能也要重新測試一遍,這導致了測試需要花非常多的時間來進行迴歸測試,這裡產生了大量重複的工作,而這些重複的工作有些是可以自動完成的,這時候UITests就可以幫助解決這個問題了。

6. 你知道的的本地資料持久化都有哪些。你比較喜歡用哪些 為什麼?

採用的資料儲存的方式有以下幾種:

1、 FMDB(常用) 2、 Sqlite(次之) 3、 Coredata(次之) 4、 NSUserdefaults(最多使用) 5、 序列化反序列化(歸檔和解檔) 6、 MongoDB(小眾型的)

  大家討論用的最多的是FMDB,原因很簡單,關係型資料庫,使用方便(相對於沒經過封裝和加工的Sqlite來說)。其次就是sqlite和coredata 當然使用者三種主要是為了快取。因為我們在開發中為了給使用者更好的體驗,就採用快取的形式。一般情況下要做的操作就是在本地建立一個資料庫(本地後臺)。

7.談談MVC設計模式的優缺點

程式設計以來就一直被灌輸MVC設計模式,具體MVC使用到底好在哪裡 又有那些不足之處,可以通過下面的介紹得以瞭解。

一、mvc原理

mvc是一種程式開發設計模式,它實現了顯示模組與功能模組的分離。提高了程式的可維護性、可移植性、可擴充套件性與可重用性,降低了程式的開發難度。它主要分模型、檢視、控制器三層。

  1. 模型(model)它是應用程式的主體部分,主要包括業務邏輯模組(web專案中的Action,dao類)和資料模組(pojo類)。模型與資料格式無關,這樣一個模型能為多個檢視提供資料。由於應用於模型的程式碼只需寫一次就可以被多個檢視重用,所以減少了程式碼的重複性

  2. 檢視(view) 使用者與之互動的介面、在web中檢視一般由jsp,html組成

  3. 控制器(controller)接收來自介面的請求 並交給模型進行處理 在這個過程中控制器不做任何處理只是起到了一個連線的做用.

二、MVC的優點

  1. 可以為一個模型在執行時同時建立和使用多個檢視。變化-傳播機制可以確保所有相關的檢視及時得到模型資料變化,從而使所有關聯的檢視和控制器做到行為同步。

  2. 檢視與控制器的可接插性,允許更換檢視和控制器物件,而且可以根據需求動態的開啟或關閉、甚至在執行期間進行物件替換。

  3. 模型的可移植性。因為模型是獨立於檢視的,所以可以把一個模型獨立地移植到新的平臺工作。需要做的只是在新平臺上對檢視和控制器進行新的修改。

  4. 潛在的框架結構。可以基於此模型建立應用程式框架,不僅僅是用在設計介面的設計中。

三、MVC的不足之處

  1. 增加了系統結構和實現的複雜性。對於簡單的介面,嚴格遵循MVC,使模型、檢視與控制器分離,會增加結構的複雜性,並可能產生過多的更新操作,降低執行效率。

  2. 檢視與控制器間的過於緊密的連線。檢視與控制器是相互分離,但確實聯絡緊密的部件,檢視沒有控制器的存在,其應用是很有限的,反之亦然,這樣就妨礙了他們的獨立重用。

  3. 檢視對模型資料的低效率訪問。依據模型操作介面的不同,檢視可能需要多次呼叫才能獲得足夠的顯示資料。對未變化資料的不必要的頻繁訪問,也將損害操作效能。

  4. 目前,一般高階的介面工具或構造器不支援模式。改造這些工具以適應MVC需要和建立分離的部件的代價是很高的,從而造成MVC使用的困難。

8.談談你對多執行緒的理解,你經常用的多執行緒有哪些實現方式,談談他們優缺點。

使用NSOperationQueue用來管理子類化的NSOperation物件,控制其執行緒併發數目。GCD和NSOperation都可以實現對執行緒的管理,區別是 NSOperation和NSOperationQueue是多執行緒的物件導向抽象。專案中使用NSOperation的優點是NSOperation是對執行緒的高度抽象,在專案中使用它,會使專案的程式結構更好,子類化NSOperation的設計思路,是具有物件導向的優點(複用、封裝),使得實現是多執行緒支援,而介面簡單,建議在複雜專案中使用。

專案中使用GCD的優點是GCD本身非常簡單、易用,對於不復雜的多執行緒操作,會節省程式碼量,而Block引數的使用,會是程式碼更為易讀,建議在簡單專案中使用。

什麼時候用多執行緒?

大多情況下,要用到多執行緒的主要是需要處理大量的IO操作時或處理的情況需要花大量的時間等等,比如:讀寫文 件、視訊影像的採集、處理、顯示、儲存等。

多執行緒的作用?

可以解決負載均衡問題,充分利用cpu資源 。為了提高CPU的使用率,採用多執行緒的方式去同時完 成幾件事情而互不干擾.

iOS實現多執行緒有哪幾種方式?

主要有三種主要方法。

1、NSThread。

2、NSOperation。

3、GCD。

多執行緒安全問題的幾種解決方案?

使用鎖。鎖是執行緒程式設計同步工具的基礎。鎖可以讓你很容易保護程式碼中一大塊區域以便你可以確保程式碼的正 確性。使用POSIX互斥鎖;使用NSLock類;使用@synchronized指令等。

分執行緒回撥主執行緒方法是什麼?有什麼作用呢?

回到主執行緒的方法:

(1). performSelectorOnMainThrea

[self performSelectorOnMainThread:<#(SEL)#>withObject:<#(id)#>waitUntilDone:<#(BOOL)#>];
複製程式碼

(2). GCD

dispatch_async(dispatch_get_main_queue(), ^{ 
});
複製程式碼

(3). NSOperationQueue

[[NSOperationQueue mainQueue]addOperationWithBlock:^{
}]
複製程式碼

作用:主執行緒是顯示UI介面,子執行緒多數是進行資料處理.

PS:最高境界是非同步單執行緒,江湖上稱協程。 可以參考 boost 中的 asio 使用者級的任務排程

9.談談你對物件導向和麵向過程的認識。

簡單對比

程式導向就像是一個細心的管家,事無具細的都要考慮到。而物件導向就像是個家用電器,你只需要知道他的功能,不需要知道它的工作原理。“程式導向”是一種是事件為中心的程式設計思想。就是分析出解決問題所需的步驟,然後用函式把這寫步驟實現,並按順序呼叫。物件導向是以“物件”為中心的程式設計思想。

簡單的舉個例子:汽車發動、汽車到站

這對於“程式導向”來說,是兩個事件,汽車啟動是一個事件,汽車到站是另一個事件,程式導向程式設計的過程中我們關心的是事件,而不是汽車本身。針對上述兩個事件,形成兩個函式,之後依次呼叫。然而這對於物件導向來說,我們關心的是汽車這類物件,兩個事件只是這類物件所具有的行為。而且對於這兩個行為的順序沒有強制要求。

兩種思想的對比

程式導向其實最為實際的一種思考方式,因為我們總是一貫一步一步的解決問題。(舉個簡單的事情,在初學物件導向的語言例如c++時,我們也總是不經意的程式導向了!)。其實就算是物件導向思想也是包含有程式導向思想的,程式導向需形成事件、也就是函式,物件導向需抽象出類,並且也會定義出這類物件的“行為”及方法。但是不論是程式導向的函式,還是物件導向的方法,兩者所完成目的都是一致的。可以說程式導向是一種基礎的方法,它考慮的是實際的實現,一般情況下,程式導向是自頂向下逐步求精,其最重要的是模組化的思想方法。物件導向的方法主要是把事物給物件化,包括其屬性和行為。這裡在程式較小的時候,程式導向就會體現出一種優勢,其程式流程十分清楚。如同上述汽車發動、到站這一過程,程式導向可以很清晰的將這一過程體現出來。而面向對像僅僅是抽象出一個Bus類,包括髮動、到站之兩個行為,具體的執行順序不能體現出來。

程式導向和麵向物件的本質理解

程式導向就是分析出解決問題所需的步驟,物件導向則是把構成問題的事物分解成物件,抽象出物件的目的並不在於完成某個步驟,而是描述其再整個解決問題的步驟中的行為。程式導向的思維方式是分析綜合,物件導向的思維方式是構造。 例如c語言解決問題時,一般是先定義資料結構,然後在構造演算法。而是物件導向求解時則是先抽象出物件,構造一個“封閉”的環境,這個環境中有定義的資料和解決問題的演算法。程式導向的設計更具挑戰性,技巧性,物件導向主要在於物件抽象的技術性,一旦完成抽象,任何人都可以做後面的工作了。從程式碼層結構上來說的話,物件導向和麵向過程的主要區別就是資料是單獨存數還是與操作儲存在一起。物件導向提供了資料的封裝後,是的對某一操作而言,資料的訪問變得可靠了。

程式導向就是將coding當做一件事,一步一步完成,物件導向就是將coding當做一件事物,需要做什麼的時候由事物(物件)本身的行為去完成。

總的來說:

  • 物件導向是將事物高度抽象化。
  • 程式導向是一種自頂向下的程式設計。
  • 物件導向必須先建立抽象模型,之後直接使用模型就行了。

程式導向就是說把做事情的步驟一步一步要幹啥清楚明瞭的告訴我們。就是說我們知道具體是通過什麼方式來實現的。

物件導向說白了就是我們只需要知道我們所使用的物件有什麼功能,然後我們讓物件去做事情。我們關心的不是實現的過程,而是能否實現和實現的結果。是事物抽象化的一種體現。

附:

  1. 答案部分摘自yykit作者ibireme: iOS 保持介面流暢的技巧

  2. 單元測試參考文章:iOS單元測試(作用及入門提升)

  3. 多執行緒可以參考文章:iOS開發全面解析多執行緒

  4. 物件導向程式導向本質參考文章:程式導向與物件導向的本質區別思考

(未完待續)

個人部落格 http://shavekevin.com/#blog

QQ交流群:214541576 歡迎大家進群交流。

相關文章