iOS中高級筆試題二

DSYMar 8年前發布 | 36K 次閱讀 iOS開發 鏈表 移動開發

來自: http://www.henishuo.com/ios-middle-hight-interview-two/

前言

最新整理的筆試題,由群里某某群友提供的題目,筆者整理并在此提供參考答案。

招聘高峰期來了,大家都非常積極地準備著跳槽,那么去一家公司面試就會有一堆新鮮的問題,可能不會,也可能會,但是了解不夠深。本篇文章為群里的小伙伴們去某公司的筆試題,由筆者整理并提供筆者個人參考答案。注意,僅供參考,不代表絕對正確。

參考答案不唯一,大家可以根據自己的理解回答,沒有必要跟筆者的一樣。參考筆者的答案,也許給你帶來靈感!

題照(前五題)

1、鏈表不具備的特點是()

A. 可隨機訪問任何一個元素

B. 插入,刪除操作不需要移動元素

C. 無需事先估計存儲空間大小

D. 所欲存儲空間可以是不連續的

這道題是考大學時所學的鏈表知識,其實筆者也忘了很多了。因為在大學時,曾經自己寫過很多鏈表相關的代碼,再加上經常幫同學調試,以及幫助同學講解鏈表相關知識點,因此記憶較深。以下答案純屬個人認知,非百度而來,若有不對之處,請指出。

參考答案: (A)

鏈表不同于數組。鏈表之所有叫鏈表,就是像一條鏈一樣,要過到某個節點處,就得遍歷著找;而數組才具備隨機訪問任何一個元素的能力,數組可以通過索引直接訪問元素,時間復雜度為常量,效率非常高,因此在某些場合上,我們需要數組這樣的數據結構。

B. 鏈表的插入、刪除都不需要移動元素,只需要修改指針的指向就可以了,因為鏈表上的每個節點都是動態分配的,分配在堆上,通過指針來指向每個節點的內存區,要獲取某個節點的值,是需要遍歷一遍才能找到對應的節點的。

C. 因為鏈表上的每個節點是分配在堆上,需要開發人員手動申請內存空間的,因此不像數組在定義時就要指定存儲空間大小。對于鏈表,需要增加一個節點時,直接在堆上申請。當需要刪除某個節點時,可以直接將該節點的內存給釋放掉。

D. 因為鏈接中的節點都是存儲在堆上的,而每個節點之間都有一個指向前一個節點和后一個節點的指針,只要知道鏈表頭指針,就可以通過遍歷查找到任何一個節點。因此,鏈表不同于數組,數組是要連續的內存存儲空間,才能保證以常量時間復雜度快速訪問任意元素;而鏈表不要求每個節點是連接,在堆上申請的內存空間很難得到連續的,而且空間產生內存碎片。

2、關于多線程和多進程編程,下面描述正確的是()

A. 多進程里,子進程可獲取父進程的所有堆和棧的數據;而線程會與同進程的其他線程共享數據,擁有自己的棧空間。

B. 線程因為有自己的獨立棧空間且共享數據,所有執行的開銷相對較大,同時不利于資源管理和保護。

C. 線程的通信速度更快,切換更快,因為他們在同一地址空間內。

D. 線程使用公共變量/內存時需要使用同步機制,因為他們在同一地址空間內。

3、設兩個變量a=19;b=29;在不創建新實例的情況下使a、b的值互換

參考答案:

這道題要求不創建新的實例,只有a、b兩個變量,要交換這兩個變量的值,通常的做法是使用臨時變量來臨時存儲,但是現在要求不使用新的實例,那么有什么辦法呢?

方法就是通過位運算來操作:

 
a = a ^ b;
b = a ^ b;
a = a ^ b;
 

對于題目中的a = 19,也就是對應二進制 00010011 ;而b=29,也就是對應二進制 00011101

  • 第一步:a = 00010011 ^ 00011101 => 00001110,將a、b的值都記錄下來了
  • 第二步:b = 00001110 ^ 00011101 => 00010011(值為19,也就是b得到了原來的a的值)
  • 第三步:a = 00001110 ^ 00010011 => 00011101 (值為29,也就是a得到了原來的b的值)

注意,

符號表示按位異或。所謂按位異或是指對應位置上的二進制數值相同為0,不同為1。

4、使用block時什么情況會發生引用循環,如何解決?

參考答案:

筆者之前寫過這篇文章講了講開發中常見的內存循環引用的案例:

iOS Block循環引用精講

5、為什么要序列化,對象序列化方式

參考答案:

筆者也不是很確定,iOS里的序列化是指歸檔、JSON序列化嗎?實際上歸檔也就是將對象轉換成XML、JSON序列化也就是將對象轉換data。

將對象JSON序列化:

 
NSLog(@"%s", __FUNCTION__);
NSDictionary *dict = @{@"key"  : @"value",
                    @"key1" : @"value1",
                    @"key2" : @"value2"};
NSData *data = [NSJSONSerializationdataWithJSONObject:dictoptions:NSJSONWritingPrettyPrintederror:nil];
NSLog(@"%@", [[NSString alloc]initWithData:dataencoding:NSUTF8StringEncoding]);
 

將對象歸檔:需要遵守NSCoding協議,實現如下方法:

 
- (void)encodeWithCoder:(NSCoder *)aCoder {
  [aCoderencodeObject:self.titleforKey:@"title"];
}
 

如果所謂的序列化不是指這兩種,還請高人指點。

題照(后三題)

7、簡述如何處理UI與耗時操作的通信,有哪些方式及各自的優缺點

參考答案:

  • 將耗時的計算和IO操作放在子線程去處理,然后到主線程更新UI。優點是
  • 采用預加載方式,將耗時操作提前處理。優點是可讓UI更流暢;缺點是內存會增多,控制加載邏輯比較復雜。
  • 采用延遲加載方式,將耗時操作而不立刻使用時,采用延遲加載。優點是界面可提高流暢度;缺點是在需要顯示時還需要加載才能顯示,需要稍稍等待。

不知道說得合不合適,還請高人提出還有哪些方式?

8、如何優化一個TableView

參考答案:

  • 若高度一定,直接使用rowHeight屬性而不是使用heightForRowAtIndexPath方法,以減少調用的消耗。若高度是不固定的,heightForRowAtIndexPath所計算的高度應該緩存起來,每次數據源發生變化時,比如刪除、插入、更新行都會重新請求所有的高度。若有100個行,就會有調用100次,因為將高度緩存起來是應該的。同理,heightForHeaderInSection、heightForFooterInSection也應該緩存起來。
  • 不要在tableView:cellForRowAtIndexPath:中做太多的計算和IO操作,比如可以將需要的計算提前計算好、IO操作也提前計算好。它應該直接調用來顯示就可以。
  • 將計算行高的時間提前到從服務器獲取數據的時候,計算完了高度一并寫回數據庫或者通過轉型為model,將高度放到模型中。但是,最好將高度緩存起來。若一個model的數據有不同的狀態,比如展開與收起狀態,應該也將高度都緩存起來。注意使用異步去計算,計算完成后再回到主線程顯示。
  • 在設置顯示圖片時,不要直接設置UIImageView的contentMode屬性自動適應,圖片變形會計算transform,壓縮時會乘以一個矩陣,消耗性能。對于要求性能較高的app,應該將得到的圖片經過處理成UIImageView大小后再呈現。
  • 不要將視圖的opaque屬性設置為NO,默認為YES,它表示不透明度。當opque為NO的時候,圖層的半透明取決于圖片和其本身合成的圖層為結果。
  • layer添加圓角是比較耗時的,這樣會離屏渲染,需要犧牲更多的性能。比如,圖片顯示有圓角時,可以通過core graphics來生成帶圓角的圖片等。
  • 手動繪制cell。繪制cell不建議使用UIView,建議使用CALayer。 UIView的繪制是建立在CoreGraphic上的,其使用的是CPU。CALayer使用的是Core Animation,CPU、GPU都可以使用且由系統自動決定使用哪一個。UIView的繪制,使用的是自下向上的一層一層的繪制,而后渲染。Layer處理的是紋理,利用GPU的 Texture Cache和獨立的浮點數計算單元可以加速紋理的處理。
  • 重用cell。防止重復的繪制,減少渲染次數,可提高性能。
  • 減少subviews的數量。盡量放在同一層view上顯示。
  • 盡量少動態給cell添加子view。用addView給Cell動態添加View,可以初始化時就添加,然后通過hide來控制是否顯示。

想要更深入,不防看看大牛的文章吧: iOS 保持界面流暢的技巧

9、假設讓你設計一關于指示器的開源庫,請設想和設計框架的public API,并指出大概需要如何做、需要注意一些什么方面來使別人容易使用這個框架

參考答案:

設計API的基本準則是:

  • 簡單易用
  • 易擴展
  • 單一功能

注意事項:

  • 盡可能不要依賴第三方庫(除非不使用第三庫需要非常大的工作量)。
  • 每個API的功能應該是單一的,只做一件事。
  • 注意性能、內存問題
  • 注意多個HUD顯示、關閉的切換問題
  • 樣式問題

分析:

要想讓全工程使用起來非常方便,那最好的方式就是使用單例,比如SVProgressHUD就是通過單例的方式來操作的。將單例封閉在內部,外部并不知道是單例,而外部的調用全是通過類方法的形式來調用,代碼調用是非常簡化的。使用單例的優點是方便管理和調用。缺點就是一直占用內存而不釋放。

當然,我們也可以通過正常的對象創建,在哪里使用就在哪里創建一個對象,自己來管理。這樣的方式在使用的地方不是那么方便,還需要再單獨進行一層封裝,以方便直接調用。但這種方式的好處就是在不需要使用的時候可以釋放掉;缺點就是如果同時創建了多個HUD來顯示時,需要調用者使用代碼邏輯來控制之前的顯示與隱藏或者切換文本等。

筆者覺得,使用單例方式更方便調用一些,外部也不用通過邏輯來管理多個HUD的顯示與隱藏問題,都封裝到內部,由封裝庫的人來維護。

假設設計一個HYBProgressHUB的小例子,隨手寫的,沒有真實寫完整:

 
typedef NS_ENUM(NSUInteger, HYBProgressHUBMaskType) {
  kHYBProgressHUBClear,
  kHYBProgressHUBBlack,
  kHYBProgressHUBUserEnabled
};
 
@interfaceHYBProgressHUD: NSObject
 
+ (void)setDefaultMaskType:(HYBProgressHUBMaskType)defaultMaskType;
 
+ (void)show;
+ (void)showWithText:(NSString *)text;
+ (void)showWithImage:(NSString *)image;
+ (void)showWithText:(NSString *)textimage:(UIImage *)image;
 
+ (void)showWithText:(NSString *)textmaskType:(HYBProgressHUBMaskType)maskType;
+ (void)showWithImage:(NSString *)image  maskType:(HYBProgressHUBMaskType)maskType;
+ (void)showWithText:(NSString *)textimage:(UIImage *)image  maskType:(HYBProgressHUBMaskType)maskType;
 
+ (void)dismiss;
+ (void)dismissWithText:(NSString *)text;
+ (void)dismissWithImage:(UIImage *)image;
+ (void)dismissWithText:(NSString *)textimage:(UIImage *)image;
 
@end
 

將單例放在實現文件中,并沒有暴露出來:

 
@implementation HYBProgressHUD
 
+ (instancetype)sharedInstance {
 __blockHYBProgressHUD *singleTon = nil;
  
  static dispatch_once_tonceToken;
  dispatch_once(&onceToken, ^{
    singleTon = [[[self class]alloc]init];
  });
  
  return singleTon;
}
 
@end
 

因為根據不同的需求,不同的場景,show會有很多種形式,比如不可點擊、可點擊、是否半透明等,因此API也應該針對單一性原則提供對應的API。但是,一個App中大部分的HUD顯示的樣式、方式是相同的,因此,我們可以提供一個默認的全局的呈現樣式,比如調用show這個API要顯示的樣式就是默認的樣式,若不調用,內部也會給一個默認值。

對于dismiss也一樣,可以是直接隱藏,也可以在隱藏之前先提示點什么,比如純文本提示、純圖片提示、文本和圖片組合提示等。

當然,我們還可以擴展API設置dismiss的默認顯示圖片和文字的樣式:

 
typedef NS_ENUM(NSUInteger, HYBProgressHUBDismissType) {
  kHYBProgressHUBDismissSuccess,
 kHYBProgressHUBDismissFailed,
  kHYBProgressHUBDismissWarnings,
  kHYBProgressHUBDismissTimeout
};
 
+ (void)dismissWithText:(NSString *)texttype:(HYBProgressHUBDismissType)type;
 

比如在所有的網絡請求的地方,就可以寫一個通用的API,在成功與失敗回調處,統一處理顯示不同的類型提示。

提示:

上面所描述內容為筆者純手工嘗試設計的,不代表足夠合理,寫出來的最主要目的是拋磚引玉,當然我更希望能夠有大神出來指出其中的不足或者加以補充,讓后來者少走彎路。

最后

本來不太想整理這份筆試題的,但是對于其中的幾道題確實很需要認真地思考,另外也希望得到大神們的評論,指出其中的缺陷,或者能夠加以補充。請各位大神們,注入新鮮的血液吧!

參考

關注我

關注 賬號 備注
Swift/ObjC技術群一 324400294 群一若已滿,請申請群二
Swift/ObjC技術群二 494669518 群二若已滿,請申請群三
Swift/ObjC技術群三 461252383 群三若已滿,會有提示信息
關注微信公眾號 iOSDevShares 關注微信公眾號,會定期地推送好文章
關注新浪微博賬號 標哥Jacky 關注微博,每次發布文章都會分享到新浪微博
關注標哥的GitHub CoderJackyHuang 這里有很多的Demo和開源組件
關于我 進一步了解標哥 如果覺得文章對您很有幫助,可捐助我!

版權聲明:本文為【標哥的技術博客】原創出品,歡迎轉載,轉載時請注明出處!

</div>

 本文由用戶 DSYMar 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!