iOS面試了20幾家總結出來的面試題(二)

27. SDWebImage是怎麼作緩存的?

  • 首先說,緩存採用了二級 緩存策略。 圖片緩存的時候, 在內存有緩存, 在磁盤中也有緩存, 其中內存緩存是用NSCache作的 (下面會有NSCache的說明)。 1、如何作緩存的步驟: 0、下載圖片 一、將圖片緩存在內存中 二、判斷圖片的格式png或jpeg,將圖片轉成NSData數據 三、獲取圖片的存儲路徑, 其中圖片的文件名是經過傳入Key通過MD5加密後得到的 四、將圖片存在進磁盤中。

2、如何獲取圖片的? 一、在內存緩存中找 二、若是內存中找不到, 會去默認磁盤目錄中尋找, 若是找不到,在去自定義磁盤目錄中尋找 三、若是磁盤也找不到就會下載圖片 四、獲取圖片數據以後, 將圖片數據從NSData轉化UIImage。其中轉化根據圖片的類型進行轉化 五、默認對圖片進行解壓縮,生成位圖圖片 六、將位圖圖片返回程序員

3、圖片是如何被解壓縮的? 一、判斷圖片是不是動態圖片,若是是,不能解壓縮 二、判斷圖片是否透明,若是是,不能解壓縮 三、判斷圖片的顏色空間是否是RGB若是不是、不能解壓縮 四、根據圖片的大小建立一個上下文 五、將圖片繪製在上下文中 六、從上下文中讀取一個不透明的位圖圖像,該圖像就是解壓縮後的圖像 七、將位圖圖像返回web

####接上說 NSCache面試

  • 這個NSCache說白了就是作緩存專用的一個系統類
  • 相似可變字典同樣,可是NSCache是線程安全的, 系統類自動作好了加鎖和釋放鎖等一系列的操做, 還有一個重要的是若是內存不足的時候NSCache會自動釋放掉存儲的對象,不須要開發者手動干預。
  • 來看一眼NSCache提供的屬性和相關方法
//名稱
@property (copy) NSString *name;

//NSCacheDelegate代理
@property (nullable, assign) id<NSCacheDelegate> delegate;

//經過key獲取value,相似於字典中經過key取value的操做
- (nullable ObjectType)objectForKey:(KeyType)key;

//設置key、value
- (void)setObject:(ObjectType)obj forKey:(KeyType)key; // 0 cost

/*
設置key、value
cost表示obj這個value對象的佔用的消耗?能夠自行設置每一個須要添加進緩存的對象的cost值
這個值與後面的totalCostLimit對應,若是添加進緩存的cost總值大於totalCostLimit就會自動進行刪除
感受在實際開發中直接使用setObject:forKey:方法就能夠解決問題了
*/
- (void)setObject:(ObjectType)obj forKey:(KeyType)key cost:(NSUInteger)g;

//根據key刪除value對象
- (void)removeObjectForKey:(KeyType)key;

//刪除保存的全部的key-value
- (void)removeAllObjects;

/*
當NSCache緩存的對象的總cost值大於這個值則會自動釋放一部分對象直到佔用小於該值
非嚴格限制意味着若是保存的對象超出這個大小也不必定會被刪除
這個值就是與前面setObject:forKey:cost:方法對應
*/
@property NSUInteger totalCostLimit;    // limits are imprecise/not strict

/*
緩存可以保存的key-value個數的最大數量
當保存的數量大於該值就會被自動釋放
非嚴格限制意味着若是超出了這個數量也不必定會被刪除
*/
@property NSUInteger countLimit;    // limits are imprecise/not strict
/*
這個值與NSDiscardableContent協議有關,默認爲YES
當一個類實現了該協議,而且這個類的對象再也不被使用時意味着能夠被釋放
*/
@property BOOL evictsObjectsWithDiscardedContent;

@end

//NSCacheDelegate協議
@protocol NSCacheDelegate <NSObject>
@optional
//上述協議只有這一個方法,緩存中的一個對象即將被刪除時被回調
- (void)cache:(NSCache *)cache willEvictObject:(id)obj;
@end**
複製代碼

countLimit注意一下這個屬性, 這個屬性就是設置最大緩存數量,啥意思呢? 這玩意就和棧差很少, 先進先出(叫什麼FIFO?)原則。好比你countLimit設置爲5 那麼當你緩存第6個對象的時候, 本來第一個就被移除了。 因此這便就有有一個風險,也可能會是面試點,爲何,經過key去取值的時候,必定要判斷一個獲取的對象是否爲nil?答:就由於頗有可能某些對象被釋放(頂)掉了。算法

又又又可能出現的面試題!NSCache裏面緩存的對象,在什麼場景下會被釋放?數據庫

  • 回答以前,先說一狀況,在某C中建立了NSCache對象,點擊手機的Home或者任何方式進入後臺,會發現NSCache中的代理方法被執行了,因而NSCache對象會釋放掉全部對象,還有的是,若是發生內存警告也會釋放掉全部對象。因此, 這道題應該以下這麼回答!
  • NSCache自身釋放了,其中存儲的對象也就釋放了。
  • 手動調用釋放方法removeObjectForKey、removeAllObjects
  • 緩存對象個數大於countLimit
  • 緩存總消耗大於totalCostLimit
  • 程序進入後臺
  • 收到內存警告

28.SDWebImage實現原理是什麼? 它是如何解決tableView的複用時出現圖片錯亂問題的呢

  • 原理如上,
  • 錯亂是在UIImageView+WebCache文件中這個方法每次都會調用 [self sd_cancelCurrentImageLoad];

###29. 爲何刷新UI要在主線程操做數組

  • UIKit並非一個線程安全的類,因此涉及多個線程同時對UI進行操做會形成影響。緩存

  • 爲何不把UIKit框架設置爲線程安全呢?安全

  • 由於線程安全須要加鎖,咱們都知道加鎖就會消耗性能,影響處理速度,影響渲染速度,咱們一般本身在寫@property時都會寫nonatomic來追求高性能高效率。bash

  • 假設可以異步設置view的屬性,那咱們到底是但願這些改動可以同時生效,仍是按照各自runloop的進度去改變這個view的屬性呢?服務器

  • 假設UITableView在其餘線程去移除了一個cell,而在另外一個線程卻對這個cell所在的index進行一些操做,這時候可能就會引起crash。

  • 若是在後臺線程移除了一個view,這個時候runloop週期尚未完結,用戶在主線程點擊了這個「將要」消失的view,那麼究竟該不應響應事件?在哪條線程進行響應?

  • 在Cocoa Touch框架中,UIApplication初始化工做是在主線程進行的。而界面上全部的視圖都是在UIApplication 實例的葉子節點(內存管理角度),因此全部的手勢交互操做都是在主線程上才能響應

30. RunTime

類的結構體:

//Class也表示一個結構體指針的類型
typedef struct objc_class *Class;

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

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

} OBJC2_UNAVAILABLE;
複製代碼

分類結構體

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods; // 對象方法
    struct method_list_t *classMethods; // 類方法
    struct protocol_list_t *protocols; // 協議
    struct property_list_t *instanceProperties; // 屬性
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
複製代碼

引伸1. class_copyIvarList與class_copyPropertyList的區別?

  • 1.class_copyIvarList:可以獲取.h和.m中的全部屬性以及大括號中聲明的變量,獲取的屬性名稱有下劃線(大括號中的除外)。
  • 2.class_copyPropertyList:只能獲取由property聲明的屬性,包括.m中的,獲取的屬性名稱不帶下劃線。

引伸2. class_ro_t和class_rw_t的區別?

  • class_rw_t提供了運行時對類拓展的能力,
  • class_ro_t存儲的大可能是類在編譯時就已經肯定的信息。
  • 兩者都存有類的方法、屬性(成員變量)、協議等信息,不過存儲它們的列表實現方式不一樣。簡單的說class_rw_t存儲列表使用的二維數組,class_ro_t使用的一維數組。
  • class_ro_t存儲於class_rw_t結構體中,是不可改變的。保存着類的在編譯時就已經肯定的信息。
  • 運行時修改類的方法,屬性,協議等都存儲於class_rw_t中

31. NSNotification

  • NSNotificationCent 子線程中發出通知,也要在主線程中刷新UI
// 好比
   dispatch_async(dispatch_get_main_queue(), ^{
       // 刷新UI
   });
複製代碼
  • NSNotificationCenter用完以後不移除, 會崩潰麼?
  • 有時候會致使crash。好比在通知事件中處理數據或者UI事件,可是因爲通知的不肯定性形成事件的不肯定,有異步操做在通知事件中處理等均可能形成崩潰。
  • 並且通知的崩潰很難檢測。

32. 什麼狀況使用 weak 關鍵字,相比 assign 有什麼不一樣?(輪迴系列)

  • weak 這個詞兒解決了一件事情,就是內存的事情
  • 在ARC中weak的出現解決了一些循環引用的問題, 好比delegate, xib連線出來的控件通常也是weak(也能夠用strong )
  • weak代表了一種「非擁有的關係」,不保留新值,也不釋放舊值。
  • assign也是如此,但經常使用的assign通常用於基本數據類型(CGFloat 或 NSlnteger等)
  • assign能夠用於非OC對象,也能夠用於OC對象(MRC時代使用), 可是weak必須用在OC對象。

引伸 1.關鍵字copy 的用法?

  • block用Copy是MRC時代留下來的傳統。在MRC中方法內部的block是在棧區的, 使用copy能夠把它放到堆區。 在ARC中寫不寫都行,用Strong也是能夠的。
  • NSString、NSArray、NSDictionary也常用copy, 由於裏面有對應的可變的子類型,爲了確保安全性, 建議使用copy修飾

引伸 2.@property 的本質是什麼?ivar、getter、setter 是如何生成並添加到這個類中的。

  • @property = ivar(實例變量) + getter + setter
  • 自動合成

33. 說說內存管理?

  • 其實遇到這道題,挺糾結的,有些TMD面試官就是習慣搞人,從這個玩意裏面 能往死給你嗑! 你要看過相關內存管理的詳細原理,你會發現這裏面的C++操做不少,沒學過C++的人能看個八九不離十,但是也只是能說個大其概,可是內部細節仍是得用C++來講,廢話很少說, 直接上說所謂得面試答案。
  • 粗糙版本這麼回答
  • 版本一: 內存中每個對象都有一個屬於本身的引用計數器。當某個對象A被另外一個對象引用時,A的引用計數器就+1,若是再有一個對象引用到A,那麼A的引用計數器就再+1。當其中某個對象再也不引用A了,A的引用計數器會-1。直到A的引用計數減到了0,那麼就沒有人再須要它了,就是時候把它釋放掉了
  • 版本二:對象經過 alloc copy new 生成得得對象在MRC年代須要手動管理內存, 利用得技術是returnCount引用計數器,來管理對象得釋放時機,alloc建立對象引用計數器 + 1, retain持有關係 引用計數器 +1,release 引用計數器 - 1。 若是當前對象得returnCount = 0 對象就會被在dealloc方法裏面適當時機進行釋放(啥時候釋放?)若是當前returnCount大於0得時候,就會一直被持有。
  • 稍微詳細版本的,首先當 alloc copy new 生成得對象裏面 在內部底層源碼也同時和當前對象相關聯得SideTable, 其內部有三個屬性, 一個是一把自旋鎖,一個是引用計數器相關,一個是維護weak生命得屬性得表, 其中retain、release 對利用鍵值對會對當前對象得引用計數器進行加減操做(位移),若是當前引用計數器爲0得時候,其dealloc內部會刪除當前的引用計數器,而且釋放當前對象。
  • 詳情請查看www.jianshu.com/p/ef6d9bf8f…

雜項

  • 一、imageName與imageWithContentsOfFile區別?

  • imageWithContentsOfFile: 加載本地目錄圖片,並不會緩存,佔用內存小, 不能加載image.xcassets裏面的圖片資源。 相同的圖片會被重複加載到內存中

  • imageName:加載到內存中, 會緩存起來, 佔用內存較大,相同的圖片不會被重複加載到內存當中,會讀取image.xcassets的圖片圖片資源。

  • 若是不斷重複讀取同一個圖片,則使用imageName

  • 若是不須要重複讀取同一個圖片,而且須要低內存,則使用imageWithContentsOfFile

  • 2.IBOutlet連出來的視圖屬性爲何能夠被設置成weak?

  • 由於連接之Xcode 內部把連接的控件 放進了一個_topLevelObjectsToKeepAliveFromStoryboard的私有數組中,這個數組強引用這全部top level的對象 因此用weak也無傷大雅。

    1. id 爲何不能用點語法?
  • 點語法就是setter和getter方法, 然而id類 沒法肯定所指的類是什麼類型, 尋不到setter個getter方法,id類型的對象 只能用【】方法調用方法

  • 4.id和NSObject的區別?

    • id是struct objc_object結構體指針,能夠指向任何OC對象,固然不包括NSInteger等類型,由於這些數據類型不是OC對象。
  • 另外OC的基類不止有NSObject一個,還有個NSProxy虛類。因此說id類型和NSObject並非等價的。

  • 5 . OC中 Null 與 nil的區別

    • NULL是指指針是空值,用來判斷C 指針;
    • nil是指一個OC對象(指針)爲空;
    • Nil是指一個OC類爲空;
    • NSNull則用於填充集合元素;這個類只有一個方法null,而且是單例的;
  • 6 . 自旋鎖和互斥鎖

  • 相同點:都能保證同一時間只有一個線程訪問共享資源,都能保證系統安全

  • 不一樣點:

互斥鎖:若是共享數據已經有了其餘線程加鎖了,線程會進行休眠狀態等待鎖,一旦被訪問的資源被解鎖,則等待資源的線程會被喚醒。信號量dispatch_semaphore 爲互斥鎖   @synchronized是NSLock的封裝 屬於互斥鎖  互斥鎖通常用於等待時間較長的狀況
**適用於**:線程等待鎖的時間較長

自旋鎖:若是共享數據已經有其餘線程加鎖了,線程會以死循環的方式等待鎖,一旦被訪問的資源被解鎖,則等待資源的線程會當即執行。OSSpinLock 屬於自旋鎖   自旋鎖通常用於時間較短的狀況,OSSpinLock
**適用於**:線程等待鎖的時間較端
複製代碼
  • 7 . 進程和線程的區別

  • 進程是指在系統中正在運行的一個應用程序

  • 線程是進程中的一個實體,一個進程想要執行任務, 必須至少有一條線程,應程序啓動的時候會默認開啓一條線程,也就是主線程

  • 一個進程擁有多個線程

  • 8 LayoutSubviews和drawRect調用時機 LayoutSubviews調用時機

    • init初始化不會調用LayoutSubviews方法
    • addsubView 時候會調用
    • 改變一個View的frame的時候調用
    • 滾動UIScrollView致使UIView從新佈局的時候會調用
    • 手動調用setNeedsLayout或者layoutIfNeeded

drawRect調用時機

  • drawRect 掉用是在Controller->loadView, Controller->viewDidLoad 兩方法以後掉用的.因此不用擔憂在 控制器中,這些View的drawRect就開始畫了.、

  • 9 cocoaPods裏面pod install和update的區別? **pod install **

  • 通常是第一次想要爲項目添加pod的時候使用的,固然也能夠在添加和移除庫使用
  • 每次pod install的時候,pod install 回爲每個安裝的pod庫在Podfile.lock文件中寫入其版本號,而且鎖定當前版本號。
  • 若是pod install的時候,不會更新其版本庫,而是去下載新的或者移除當前版本 pod update
  • 當執行了pod update的時候,cocoaPods不會考慮Podfile.lock中的版本。直接去更新當前全部的庫到最新,而後Podfile.lock會更新這一次的版本號。
  • 10 frame和masonry哪一個性能好?爲何
  • 有的相對佈局最終都會轉換成Frame絕對佈局 中間多了一層轉換的操做

  • 11 . iOS從iOS9 - 13的特性

iOS9

從HTTP升級到HTTPS App瘦身 下面有講 這裏不贅述( App瘦身 ) 新增UIStackView

iOS10

新增通知推送相關的操做。自定義通知彈窗,自定義通知類型(地理位置,時間間隔,日曆等)

iOS11

無線 調試 齊劉海兒,導航條,安全距離等

iOS12

啓動速度優化 應用啓動速度提高40% 鍵盤響應速度提高50% 相機啓動速度提高70%

iOS13

黑暗模式 詳情請查閱 www.jianshu.com/p/0da3b107f…


2、App包以及啓動過程

App瘦身

一、App如何瘦身?

  • 刪除陳舊代碼、刪除陳舊xib/sb,刪除無用的圖片資源(檢測未使用圖片的工具LSUnusedResources
  • 無損壓縮圖片,本地音視頻壓縮。以直接減小圖片大小
  • 使用webP格式的圖片(加載速度比較慢,但能夠達到瘦身的效果)
  • 減少類名稱的長度(高性能的話能夠試一試)
  • 減小使用靜態庫
  • 一些主題類的東西提供下載功能,不直接打包在應用包裏面,按需加載資源
  • iOS9 以後的新特性 應用程序切片(App Slicing)、中間代碼(Bitcode)和按需加載資源(On Demand Resources)

Slicing: 這個過程是iOS9出來以後 不須要程序員干預的一個瘦身的過程,簡單來講就是咱們再上傳IPA包到iTunes Connect,而後AppStore會對app進行切片,切成特定的機型想要的數據,好比@3x給max用,@2x就自動剔除了。 是一個自動的過程Bitcode:是一種中間碼,若是配置了Bitcode(Xcode7之後默認開啓)的程序會在App Store Connect上被從新編譯等一系列操做,進而蘋果內部會對可執行文件進行優化,也就是說不須要咱們干預什麼東西,也操做不了, 若是後面蘋果有更牛逼的優化操做,也是蘋果的事情, 跟咱們我的開發者一毛錢關係沒有。 On Demand Resources 按需加載, 是程序員本身手動操做,說白了就是在用的時候去下載某些資源, 可是咱們本身在配置的時候都須要配置,要額外寫一些代碼啥的,等咱們提交到市場的時候, 蘋果內部會把咱們按需加載的資源從包裏面作了一些抽離操做啥的, 讓咱們的包在下載的時候更小,舉個例子,就是吃雞裏面沙漠地圖若是玩家不本身下載, 就玩不了沙漠。

**on-demond resource(ODR)**具體請查看原理版本:www.jianshu.com/p/bacedd8a3…
或者詳細使用版本:www.cocoachina.com/articles/12…

關於 **slicing, bitcode, on-demond resource(ODR)**的參考資源https://blog.csdn.net/zhuod/article/details/70051514?utm_source=blogxgwz6


二、app啓動時候都經歷了什麼?

啓動分爲兩種。 一種是以前啓動過,按了一下home鍵,而後再點啓動,這個啓動叫熱啓動,另外就是第一次啓動app,或者啓動殺死以後的app 叫作冷啓動

根據info.plist裏面的設置加載,創建沙箱,權限檢查等 加載可執行文件 加載動態庫 objc運行時的初始化處理(類的註冊,category註冊,selector惟一性檢查等等) 初始化,包括+load方法 執行main函數 Application 初始化,到 applicationDidFinishLaunchingWithOptions 執行完 渲染屏幕,到viewDidAppear 執行完畢,展示給用戶

  • mian以前

根據info.plist裏面的設置加載,創建沙箱,權限檢查等 加載可執行文件 加載動態庫 objc運行時的初始化處理(類的註冊,category註冊,selector惟一性檢查等等) 初始化,包括+load方法

  • mian以後
    • 如圖
    • 加載流程以下:
    圖丟了!!!!!百度去吧!

三、優化啓動時間

  • 啓動時間是用戶點擊App圖標,到第一個界面展現的時間。

注意:啓動時間在小於400ms是最佳的,由於從點擊圖標到顯示Launch Screen,到Launch Screen消失這段時間是400ms。啓動時間不能夠大於20s,不然會被系統殺掉。

  • 以main函數做爲分水嶺,啓動時間其實包括了兩部分:
    • main函數以前(分析並加載動態庫,註冊須要的類(包括系統的類),Category中的方法也會註冊到對用的類中,執行必要的初始化方法( +load方法)等等
    • main函數到第一個界面的viewDidAppear:
  • 因此,優化也是從兩個方面進行的,我的建議優先優化後者,由於絕大多數App的瓶頸在本身的代碼裏。

mian函數以前的啓動優化

  • 減小動態庫的數量(這是目前爲止最耗時的了, 基本上佔了95%以上的時間)
  • 合併動態庫,好比本身寫的UI控件合併成本身的UIKit
  • 確認動態庫是optional仍是required。若是該Framework在當前App支持的全部iOS系統版本都存在,那麼就設爲required,不然就設爲optional,由於optional會有些額外的檢查
  • 合併Category(UIView+Frame,UIView+AutoLayout合併成一個)
  • 將沒必要需在+load方法中作的事情,延時放到+initialize。

mian函數以後的啓動優化 首先分析一下從main函數開始執行,到第一個頁面顯示, 這段時間作了哪些事情

  1. 執行didFinishLaunchingWithOptions方法
  2. 初始化Window,初始化基礎ViewContreoller(通常是UINavigationController+UITabViewController)
  3. 獲取數據(本地和遠程)
  4. 最後展現給用戶
  5. 減小建立線程(高性能iOS開發一書中提到,線程不只僅有建立時的時間開銷,還會消耗內核的內存,即應用的內存空間。 每一個線程大約消耗 1KB 的內核內存空間線程建立的耗時(不包含啓動時間),其區間範圍在 4000~5000 微秒,即 4~5 毫秒。建立線程後啓動線程的耗時區間爲 5~100 毫秒,平均大約在 29 毫秒。這是很大的時間開銷,若在應用啓動時開啓多個線程,則尤其明顯。線程的啓動時間之因此如此之長,是由於屢次的上下文切換所帶來的開銷。因此線程在開發過程當中也避免濫用)
  6. 合併或者刪減沒必要要的類(或者分類)和函數objc的類越多,函數越多啓動越慢
  7. 在設計師可接受的範文儘可能使用小的圖片
  • AppDelegate 一般優化的通常來講,仍是從AppDelegate先入手優化
didFinishLaunchingWithOptions
applicationDidBecomeActive
複製代碼

優化的核心思想就是,能延時的延時, 不能延時的儘可能放到後臺去優化。

- 日誌、統計等必須在 APP 一啓動就最早配置的事件。仍然把它留在 didFinishLaunchingWithOptions 裏啓動。
- 項目配置、環境配置、用戶信息的初始化 、推送、IM等事件,這些功能在用戶進入 APP 主體的以前是必需要加載完的,把他放到廣告頁面的viewDidAppear啓動。
- 其餘 SDK 和配置事件,因爲啓動時間不是必須的,因此咱們能夠放在第一個界面的 viewDidAppear 方法裏,這裏徹底不會影響到啓動時間。
- 每次用NSLog方式打印會隱式的建立一個Calendar,所以須要刪減啓動時各業務方打的log,或者僅僅針對內測版輸出log
- 儘可能不要在didFinishLaunchingWithOptions 裏面建立和開啓多線程
複製代碼

參考文獻https://www.jianshu.com/p/f40fdd8799b8 其文章內部做者談到了美團關於啓動優化的相關分析,看似似曾類似,沒記錯的畫《高性能iOS應用開發》這本書就是美團這幾個哥們兒翻譯的吧,實現方式和書中頗爲類似。


三、App電量消耗

  • 1.定位
  • 2.網絡請求
  • 3.CPU處理
  • 4.GPU處理
  • 5.Bluetooth

定位優化

1.儘可能不要實時更新 2.定位精度儘可能不要過高

網絡優化

1.減小、壓縮網絡數據 2.能使用緩存就使用緩存,減小網絡請求 3.斷點續傳 4.批量傳輸 5.設置適合的超時時間,用戶能夠取消耗時的網絡請求 6.網絡不可用時就不要再執行網絡請求了

CPU/GPU優化

相關離屏渲染操做盡可能避免 內存管理處理好 使用懶加載 使用繪製 圖片與imageView相同大小避免多餘運算 Timer的時間間隔不宜過短,知足需求便可 線程適量,不宜過多,不要阻塞主線程 適當使用多線程 減小視圖刷新:確保必要的時候才刷新,能刷新1行cell最好只刷新一行;

爲了優化耗電咱們還能夠作: 1.儘可能不要使用定時器 2.優化I/O操做(文件的讀寫操做) 2.1最好不要頻繁讀寫小數據,最好批量讀寫 2.2數據量比較大的時候能夠考慮使用數據庫 2.3讀寫大量重要數據時,考慮用dispatch_io,其提供了基於GCD的異步操做文件I/O的API。用dispatch_io系統會優化磁盤訪問

高性能iOS應用開發中提到一下幾點

  • 一、CPU優化
  • 數據處理(例如文本格式優化)
  • 待處理的數據大小----更大的顯示屏容許軟件在單個視圖中展現更多的信息,但這也意味着要處理更多的數據
  • 處理數據的算法和數據結構
  • 執行更新的次數,尤爲是在數據更新以後,觸發應用的狀態或者UI進行更新(好比刷新單行cell)
  • 服務器中的數據儘可能不要在客戶端上處理(例如服務器字符串,在客戶端進行拆分操做)
  • 按需加載(例如tableViewcell 不須要一會兒所有渲染,快速滑動的時候 過程當中的留白處理。)
  • 二、網絡
  • 在進行網絡請求以前,先檢查是否有網絡鏈接。(沒網絡的時候,不要請求網絡)
  • 避免沒有鏈接WiFi的狀況下進行高帶寬的消耗操做(由於3G、4G等手機網絡耗電量遠大於WIFi信號),例如視頻流在4G或者非Wifi狀況下應該給出響應的提示。
  • 三、定位
  • 儘可能不要實時更新
  • 定位精度儘可能不要過高

##3、算法

定義相關

  1. 鏈表和數組的區別是什麼? 鏈表和字典的區別是什麼?

數組在內存中是逐個存放的,鏈表每隔節點沒有相對固定的位置關係 數組被聲明後,大小就固定了,不能進行動態擴充。 鏈表能夠動態生成節點,而且添加到已有的鏈表後面 數組存在越界問題,鏈表則不存在 數組的插入刪除的時間複雜度是O(n),鏈表O(1) 數組的查詢下標時間複雜度爲O(1), 鏈表爲O(n) 根據值查詢的時間複雜度,鏈表和數組都是O(n)

  1. 如何檢測鏈表中是否有環?

思路 假設有兩個學生A和B在跑道上跑步,兩人從相同起點出發,假設A的速度爲2m/s,B的速度爲1m/s,結果會發生什麼? 答案很簡單,A繞了跑道一圈以後會追上B! 將這個問題延伸到鏈表中,跑道就是鏈表,咱們能夠設置兩個指針,a跑的快,b跑的慢,若是鏈表有環,那麼當程序執行到某一狀態時,a==b。若是鏈表沒有環,程序會執行到a==NULL,結束。

###一、 調換A和B

//    int a = 10;
//    int b = 20;
//
//    a = a + b;
//    b = a - b;
//    a = a - b;
//
//    NSLog(@"a = %d , b = %d", a, b);
//
//
//    a = a*b;
//    b = a/b;
//    a = a/b;
//
//    NSLog(@"1 ===== : a = %d , b = %d", a, b);
複製代碼

###二、最大公約數

//    int n = 20,v = 30,temp = 0,max,min;
//
//    if (n>v) {
//        max = n;
//        min = v;
//    } else {
//        max = v;
//        min = n;
//    }
//
//
//    while (min != 0) {
//        temp = max - min;
//        max = min;
//        min = temp;
//    }
//
//    NSLog(@"%d", max);
複製代碼

###三、打印2 - 100 的素數(質數) 除了1和自身被整除的.

//    NSMutableArray *primeNumberArray =[NSMutableArray array];
//    for(int i=2; i<=100; i++) {
//
//        NSInteger n = 0;
//
//        for(int j = 1; j <= i; j++) {
//
//            if(i % j == 0) {
//                n = n + 1;
//            }
//        }
//
//        if(n == 2) {
//            [primeNumberArray addObject:@(i)];
//        }
//    }
//
//    NSLog(@"primeNumber = %@",primeNumberArray);
//
複製代碼

###四、 字符串倒敘

//    NSString *string = @"hei Son 我是你father";
//    NSMutableString *string1 = [NSMutableString string];
//    for (NSInteger i = string.length; i>0; i--) {
//        [string1 appendString:[string substringWithRange:NSMakeRange(i -1,1)]];
//    }
//
//    NSLog(@"%@", string1);
//
複製代碼

###五、 尋找出字符串中有那些中文

//    for (int i = 0; i < string.length; i++) {
//        NSString *str1 = [string substringWithRange:NSMakeRange(i, 1)];
//        const char *cStr = [str1 UTF8String];
//        if (strlen(cStr) == 3 ) {  // oc中 中文三個字節
//            NSLog(@"%@", str1);
//        }
//    }
複製代碼

####6. 排序

  • 冒泡排序

比較相鄰的元素。若是第一個比第二個大,就交換他們兩個。 對每一對相鄰元素作一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。 針對全部的元素重複以上的步驟,除了最後一個。 持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較

for (int i = 0; i<result.count-1; i++) {
        for (int j = 0; j<result.count-1-i; j++) {
            NSInteger left = [result[j] integerValue];
            NSInteger right = [result[j+1] integerValue];
            if (left>right) {
                [result exchangeObjectAtIndex:j withObjectAtIndex:j+1];
            }
        }
    }
NSLog(@"%@",result);
時間複雜度O(n^2)
複製代碼
  • 選擇排序

選擇排序就是經過遍歷數組找出每次遍歷數組的最小元素的下標,而後將其按順序從第一位依次排列

//self.array = @[@2,@4,@3,@1];
     NSMutableArray *mutableArray = [self.array mutableCopy];//oc數組中不能存儲基本數據類型,因此快速賦值完成後,系統默認數組元素爲NSNumber類型
    if (mutableArray == nil || [mutableArray count] == 0)
    {
        return;
    }
    for (int i = 0; i < [mutableArray count]; i++)
    {
        NSInteger minIdx = i;//默認最小值的索引爲i
        for (int j = i+1; j < [mutableArray count]; j++)//經過循環尋找當前數組中最小值的索引值
        {
            if (NSOrderedAscending == [mutableArray[j] compare:mutableArray[minIdx]])//NSNumber類判斷大小方法,這句話的意思是當mutableArray[j] <mutableArray[minIdx]時
            {
                minIdx = j;//更新數組中最小值的索引值
            }
        }
        [mutableArray exchangeObjectAtIndex:i withObjectAtIndex:minIdx];//將每次循環結束後找到的最小值交換到數組的第i位
        NSLog(@"%@",mutableArray);
    }
}
時間複雜度爲O(n)。最壞狀況下仍爲O(n^2)
複製代碼
  • 升序/降序
NSMutableArray *priceArray = [NSMutableArray arrayWithObjects:@"0.2",@"5",@"44",@"67",@"98.5",@"1.55", nil];
 
[priceArray sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2)  {
     if ([obj1 integerValue] < [obj2 integerValue]){
         return NSOrderedAscending;
     } else {
       return NSOrderedDescending;
     }
}];
 
這裏再次獲得的priceArray即爲升序排列的數組;
若果想要獲得降序的調換一下return的位置便可
複製代碼

####7 尋找最近的公共View

// 全部View的父View
+ (NSArray *)superViews:(UIView *)view{
    if (view==nil) {
        return @[];
    }
    NSMutableArray *result = [NSMutableArray array];
    while (view!=nil) {
        [result addObject:view];
        view = view.superview;
    }
    return [result copy];
}

// 相互對比兩個節點中的view,出現同樣就返回
+ (UIView *)commonView_1:(UIView *)viewA  andView:(UIView *)viewB{
    NSArray *arr1 = [self superViews:viewA];
    NSArray *arr2 = [self superViews:viewB];
    for (NSUInteger i =0; i<arr1.count; ++i) {
        UIView *targetView = arr1[i];
        for (NSUInteger j=0; j<arr2.count; ++j) {
            if (targetView == arr2[j]) {
                return targetView;
            }
        }
    }
    return nil;
}


// 利用NSSet中的hash表,能夠將上面代碼進行進一步優化
+ (UIView *)commomView_2:(UIView *)viewA andView:(UIView *)viewB{
    NSArray *arr1 = [self superViews:viewA];
    NSArray *arr2 = [self superViews:viewB];
    NSSet *set = [NSSet setWithArray:arr2];
    for (NSUInteger i =0; i<arr1.count; ++i) {
        UIView *targetView = arr1[i];
        if ([set containsObject:targetView]) {
            return targetView;
        }
    }
   return nil;
}
複製代碼

####8.數組題:如何在有序數組中找出和等於給定值的兩個元素?

NSArray *arr = @[@"1", @"12", @"13", @"23", @"31", @"43", @"52", @"66", @"88", @"111", @"127", @"199"];
[self confirmNumbers:arr total:199];
----


- (void)confirmNumbers:(NSArray *)array total:(NSInteger)totalNmuber  {
    if (array.count <= 1) { return; }

    NSInteger tempAddCount = 0;
    NSInteger tempDeleltCount = 0;

    for (int i = 0; i <= array.count; i++) {
        NSInteger tNamber = [array[tempAddCount] integerValue] + [array[array.count-1-tempDeleltCount] integerValue];
        if (tNamber == totalNmuber) {
            NSLog(@"%ld, %ld, 第一個元素%@ - 後面的元素%@", (long)tempAddCount, (long)tempDeleltCount, array[tempAddCount] ,array[array.count-tempDeleltCount-1]);
            break;
        } else if (tNamber < totalNmuber) {
            tempAddCount ++;
        } else {
            tempDeleltCount ++;
        }
        if (i == array.count - 1) {
            NSLog(@"啥都沒匹配着");
            break;
        }
    }
 
    
}
複製代碼

9 用遞歸寫一個算法,計算從1到100的和。

NSLog(@"%ld", [self getSumResult:100]);

- (NSInteger)getSumResult:(NSInteger)number {
    if (number <=0 ) {
        return number;
    }
    
    return number + [self getSumResult:number - 1];
}
// 遞歸效率差的緣由是 每一次調用函數(本身)都是要有內存開銷的,影響CUP的效率
複製代碼

10.打亂一個數組

NSArray* arr = @[@"1",@"2",@"3"];
    arr = [arr sortedArrayUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
        int seed = arc4random_uniform(2);
        if (seed) {
            return [str1 compare:str2];
        } else {
            return [str2 compare:str1];
        }
    }];
複製代碼

##3、軟技術篇 ###1.開發過程當中, 你碰到那些技術難點?是怎麼解決的?

  • 其實這個題主要仍是面試官看想了解你的真實項目經驗, 若是你回答的東西,根本就算不上是什麼技術問題,而是基礎問題那估計也沒什麼聊下去的必要了, 舉個例子,你回答的是「我在開發過程當中老是發現UITableView這個控件寫起來比較麻煩, 並且還老是報數組越界的問題」 那麼對不起, 你最多能找一個實習的工做。由於你能說出這個問題,我估計你上面的硬核面試題,一半兒都不知道咋回事!
  • 其實這題也沒那麼高大上,說一下你真實開發過程當中遇到的難點,說的越高大上越好, 最好是把面試官說懵逼了,固然若是你有自信的話,最好別誇大,萬一你趕上一個剛好在這個領域人家更牛逼呢?
  • 舉個例子「我在開發咱們項目的時候, 涉及到圖像處理的問題,就好比說如今網絡上比較火的用SDWebImage下載超清大圖的時候出現的崩潰問題,由於decodeImageWithImage 這個方法用於對圖片進行壓縮而且緩存起來,以保證tableview/collectionview交互更加流暢,可是若是用此方法加載超清大圖的時候, 會拔苗助長,有可能致使上G的內存消耗, 解決辦法是對於高清圖片,應該放在圖片解壓以後,禁止緩存解壓後的數據。 代碼以下
- SD4.X的解決辦法
[[SDImageCache sharedImageCache] setShouldDecompressImages:NO];
[[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];


- SD5.0 及以上的解決辦法
SDWebImageAvoidDecodeImage添加了這個枚舉,意思是在子線程程解壓縮圖片
[self.imageView sd_setImageWithURL:self.url placeholderImage:[UIImage imageNamed:@"logo"] options:SDWebImageAvoidDecodeImage];
複製代碼
  • 更多詳情請查看 也必須看!

  • www.jianshu.com/p/b36e96d6d…

  • www.jianshu.com/p/64be3aed2…

  • 在舉個例子「因爲在開發過程當中,用到的全部圖片必須是原圖, 問題就出現了, 若是幾張圖片是超清的超大圖片,把這些圖片所有渲染到一個畫布中而且進行隨機形狀的超高清拼圖, 這個過程會出現一個奇怪的問題,就是繪製的結果是大機率會變成 一張純白色的沒有任何圖案的圖片,出現的緣由是由於在App內部若是正在運行的內存達到必定的值得時候繪製圖像的上下文就會獲取一個空白的圖片,解決辦法由於是機率事件因此內部作了一個循環渲染的機制,在特定次數範圍內,若是出現繪製成功的話返回正常的圖片,若是沒有正確繪製,則作一個內部的提示語App內部沒有任何反應, 因此若是製做跟圖片相關的項目,特別涉及到自定義系統相冊的功能,最好優化好內存問題,由於內存優化很差,致使的問題有不少系統層級的BUG。並且很難找到問題的緣由

  • ** 這就把面試官引向了一個優化內存的事情。好比優化內存的工具, 檢測內存泄漏,循環引用的工具等等。 下面會一一介紹。**

###2.過程當中, 你用過什麼調試工具?

  • instrument
Leaks(泄漏):通常的查看內存使用狀況,檢查泄漏的內存,並提供了全部活動的分配和泄漏模塊的類對象分配統計信息以及內存地址歷史記錄;
- locations(內存分配):跟蹤過程的匿名虛擬內存和堆的對象提供類名和可選保留/釋放歷史; 
複製代碼

查看更多

結語

這是陸陸續續面試中總結出來的, 總之,有的問題回答的比較撿漏, 有的問題模棱兩可,有些面試官的反饋比較紮實,有些面試官說回答的泰國籠統不夠細緻,反正這東西見仁見智吧。

最後附上一個寫的很詳細的基礎知識博主寫的一些列,我的以爲幫助很大!juejin.im/user/5e09e4…

此文章基本上不會添加新的問題了,若是有錯誤我會及時改正,避免耽誤有須要幫助的小夥伴! 最後一次更新時間:2020年07月20日

相關文章
相關標籤/搜索