02-面試iOS工程師常被問到的20個基礎問題

1.什么是 ARC ? ( ARC 是為了解決什么問題誕生的 ? )####

自動引用計數 ( ARCAutomatic Reference Counting ): 是指內存管理中對引用采取自動計數的技術,在Objective-C 中采用 ARC 機制,讓編譯器來進行內存管理. 在新一代 Apple LLVM編譯器中設置 ARC 為有效狀態,就無需再次鍵入 retainrelease 代碼,這在降低程序崩潰、內存泄漏等風險的同時,很大程度上減少了開發程序的工作量.編譯器完全清楚目標對象,并能立刻釋放那些不再被使用的對象.

ARC 是為了解決什么問題誕生的:需追溯到 MRC 手動內存管理時代.MRC 內存管理存在的問題:

  • 當我們要釋放一個堆內存時,首先要確定指向這個堆空間的指針都被 release 了( 避免提前釋放 )
  • 釋放指針指向的堆空間,首先要確定哪些指針指向同一個堆,這些指針只能釋放一次(MRC 下即誰創建,誰釋放,避免重復釋放 )
  • 模塊化操作時,對象可能被多個模塊創建和使用,不能確定最后由誰去釋放.
  • 多線程操作時,不確定哪個線程最后使用完畢

2. 請解釋以下 Keywords 的區別: assign 和 weak,block 和 weak?####

assignweak

  • assign 適用于基本數據類型( 如 NSInteger )和 C 數據類型( intfloatdoublechar等),另外還有idweak適用于 NSObject 對象,并且是一個弱引用.( 反正記住:前面不需要加‘ * ’的就用assign
  • assign 也可以用來修飾對象,但為什么不用它修飾對象呢 ?因為assign修飾的對象( 一般編譯的時候會產生警告:Assigning retained object to unsafe property:object will be released after assignment )在釋放之后,指針的地址還是存在的,也就是說指針并沒有被置為 nil,造成野指針. 對象一般分配在堆上的某塊內存,如果在后續內存分配中,剛好分配了這塊內存,程序就會崩潰.
  • 為什么可以用assign 修飾基本數據類型 ? 因為基礎數據類型一般分配在棧上,棧的內存會由系統自動處理,不會造成野指針錯誤.
  • weak 修飾的對象在釋放之后,指針地址會被置為nil. 這樣再向 weak 修飾的屬性發送消息就不會導致野指針操作 crash.
    weak 使用場景:
    • ARC下,在有可能出現循環引用的時候,往往要通過讓其中一端使用 weak 來解決,比如:delegate代理屬性,通常就會聲明為 weak.
    • 自身已經對它進行一次強引用,沒有必要再強引用一次時也用 weak .比如:自定義IBOutlet 控件屬性一般使用 weak,當然也可以使用 strong.

__block__weak

  • __block 不管是 ARC 還是 MRC 模式下都可以使用,可以修飾對象,還可以修飾基本數據類型.
  • __weak 只能在 ARC 模式下使用,也只能修飾對象(NSString),不能修飾基本數據類型(int).
  • __block 對象可以在 block 中被重新賦值,__weak 不可以.
  • __block 對象在 ARC 下可能會導致循環引用,非ARC下會避免循環引用,__weak 只在 ARC 下使用,可以避免循環引用.
  • __unsafe_unretained 修飾符可以被視為 iOS SDK 4.3以前版本的 __weak 的替代品,不過不會被自動置為 nil,所以盡可能不要使用這個修飾符.

參考文章:
正確使用Block避免Cycle Retain和Crash - Cooper's Blog
參考書籍:《 Objective-C 高級編程 iOS 與 OS X 多線程和內存管理 - 第2章 Blocks 》


3. __block 在 ARC 和非 ARC 下含義一樣嗎 ?####

不一樣,標記為 __block 的變量,在 block中使用,在 ARC 下會 retain,在非 ARC下則不會 retain.


參考鏈接:iOS: ARC和非ARC下使用Block屬性的問題


4.使用 atomic 一定是線程安全的嗎 ?####

nonatomic 的內存管理語義是非原子的,非原子的操作本來就是線程不安全的,而 atomic 的操作是原子的,但是并不意味著它是線程安全的,它會增加正確的幾率,能夠更好的避免線程的錯誤,但它仍然是線程不安全的.
當使用 nonatomic的時候,屬性的 settergetter 操作是非原子的,所以當多個線程同時對某一屬性進行讀和寫的操作,屬性的最終結果是不能預測的.

當使用 atomic 時,雖然對屬性的讀和寫是原子的,但是仍然可能出現線程錯誤:當線程 A 進行寫操作,這時其他線程的讀或寫操作會因為該操作的進行而等待,當 A 線程的寫操作結束后,B 線程進行寫操作,然后當 A 線程進行讀操作時,卻獲得了在 B 線程中的值,這就破壞了線程安全,如果有線程 C 在 A 線程讀操作前 release 了該屬性,那么還會導致程序崩潰.所以僅僅使用 atomic 并不會使得線程安全,我們還需要為線程添加lock來確保線程的安全.

注意atomic 所說的線程安全只是保證了gettersetter 存取方法的線程安全,并不能保證整個對象是線程安全的。如下列所示:@property(atomic,strong)NSMutableArray *arr; 如果一個線程循環的讀數據,一個線程循環寫數據,那么肯定會產生內存問題,因為這和settergetter 沒有關系,如使用 [ self.arrobjectAtIndex:index ] 就不是線程安全的. 好的解決方案就是加鎖


參考鏈接:Objective-C 之 atomic 一定是線程安全的嗎?


5.描述一個你遇到過的 retain cycle 例子####

typedef void ( ^blk_t )( void );
@interface MyObject : NSObject
{
     blk_t blk;
     id  obj_;
}
@implementation MyObject
- ( id ) init
{
     self = [ super init ];
     blk_ = ^ { NSLog ( @" obj_ = %@ " ,** obj_ **); }
     return self;
}

雖然 Block 內沒有使用self也同樣截獲了 self,引起了循環引用,即Block語法內使用的obj_實際上截獲了self,對編譯器來說,obj_只不過是對象用結構體的成員變量.相當于blk_ = ^ { NSLog( @" obj_ = %@ " , self -> obj_ ); }
為了避免循環引用,聲明附有 __weak 修飾符的變量并賦值 obj_使用,也可安全的使用__unsafe__unretained修飾符.

- ( id ) init
{
     self = [ super init ];
     id __weak obj = obj_;
     blk_ = ^ { NSLog ( @" obj_ = %@ " , **obj** ); }
     return self;
}

6. +(void)load; 和 +(void)initialize; 有什么用處 ?####

**Objective-C 中絕大多數類都繼承自 NSObject,該類有兩個方法,+load 和 +initialize,用來實現類的初始化操作. **

** +load() : **該方法是在類或者分類被添加到 Objective-C runtime時被調用的,而且只調用一次. 當包含類或分類的程序庫載入系統時,就會執行此方法,這通常就是指應用程序啟動的時候,load方法務必實現的精簡一些,也就是要減少其所執行的操作,因為整個應用程序在執行 load方法時都會阻塞.
注意:

  • 子類的 +load 方法會在它的所有父類的 +load方法之后執行,而分類的 +load方法會在它的主類的+load 方法之后執行,但是不同類的+load方法的調用順序是不確定的.
  • load 方法并不像普通的方法那樣,它并不遵循那套繼承規則,如果某個類本身沒有實現load 方法,那么不管其各級超類是否實現此方法,系統都不會調用.
  • load 方法執行時,運行時系統處于脆弱狀態,在執行子類的 load方法之前,必定會執行所有超類的load 方法,而如果代碼還依賴其他程序庫,那么程序庫里相關類的load方法也必定會先執行,然而由第一條知根據某個給定的程序庫,卻無法判斷出其中各個類的載入順序,因此在load 方法中使用其他類是不安全的.
  • 分類和其所屬的類里,都可能出現load方法,此時兩種實現代碼都會調用,類的實現要比分類的實現先執行.

+initialize()該方法是在類或它的子類收到第一條消息之前被調用的,這里的消息包括實例方法和類方法的調用,也就是說 +initialize方法是以懶加載的方式被調用的,如果程序一直沒有給某個類或它的子類發送消息,那么這個類的 initialize 方法永遠不會被調用,這樣可以節省系統資源,避免浪費.
注意:

  • 該方法會在程序首次用該類之前調用,且只調用一次. 它是由運行期系統來調用的,絕不應該通過代碼直接調用.
  • 它是“惰性調用”,就是說只有當程序用到相關類時才會調用.也就是說應用程序無需先把每個類的 initialize 都執行一遍,這與 load方法不同,對于 load 方法來說,應用程序必須阻塞并等著所有類的 load 都執行完才能繼續.
  • initialize方法與其他消息一樣,如果某個類未實現它,而其超類實現了,那么就會運行超類的實現代碼.
    要點:
  • 在加載階段,如果類實現了 load 方法,那么系統就會調用它. 分類里也可以定義此方法,類的 load 方法要比分類中的先調用. 與其他方法不同,load 方法不參與覆寫機制.
  • 首次使用某個類之前,系統會向其發送 initialize 消息,由于此方法遵從普通的覆寫規則,所以通常應該在里面判斷當前要初始化的是哪個類.
  • loadinitialize 方法都應該實現的精簡一些,這有助于保持應用程序的響應能力,也能減少引入“依賴環”的幾率.
  • 無法在編譯期設定的全局變量,可以放在 initialize 方法里初始化.

參考鏈接:
Objective-C +load vs +initialize - 雷純鋒的技術博客
Objective C類方法load和initialize的區別 - Ider - 博客園

參考書籍:Effective Objective-C 2.0 編寫高質量的 iOS 與 OS X 代碼的52個有效方法 (第51條)


7.為什么其他語言里叫函數調用,OC 中則是給對象發送消息(或者談下對 runtime 的理解).

首先,OC是一門動態語言,可以在運行的時候動態決定調用哪個方法實現,甚至添加、替換方法的具體實現,而這些都歸功于Objective-C的運行時runtime系統.

消息與函數之間的關鍵區別在于:使用消息結構的語言,其運行時所應執行的代碼有運行環境來決定,使用函數調用的語言,則有編譯器決定;

  • 發送消息的含義

[receiver message] //向 receiver 對象發送 message 消息
會被編譯器轉化為:objc_msgSend (receiver, selector);如果消息含有參數,轉化為objc_msgSend ( receiver, selector,arg1,arg2,... );如果消息的接收者能夠找到對應的selector,那么就相當于直接執行了接收者這個對象的特定方法;否則,消息要么被轉發,或是臨時向接收者動態添加這個selector對應的實現內容,要么就干脆玩完崩潰掉.
可以看出[receiver message]真的不是一個簡簡單單的方法調用,因為這只是在編譯階段確定了要向接收者發送message這條消息,而receive將要如何響應這條消息,那就要看運行時發生的情況來決定了.

  • 關于runtime

Objc Runtime使得 C 具有了面向對象能力,在程序運行時創建,檢查,修改類、對象和它們的方法,可以使用runtime的一系列方法實現; 順便附上 OC 中一個類的數據結構 /usr/include/objc/runtime.h

struct objc_class {
    //isa指針指向Meta Class,因為Objc的類的本身也是一個Object,為了處理這個關系,runtime就創造了Meta Class,當給類發送[NSObject alloc]這樣消息時,實際上是把這個消息發給了Class Object
    Class isa  OBJC_ISA_AVAILABILITY;

    #if !__OBJC2__
    // 父類
    Class super_class                                        OBJC2_UNAVAILABLE;
    // 類名
    const char *name                                         OBJC2_UNAVAILABLE;
    //類的版本號,默認為0
    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;
    // 方法緩存,對象接到一個消息會根據isa指針查找消息對象,這時會在methodLists中遍歷,如果cache了,常用的方法調用時就能夠提高調用的效率。
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    // 協議鏈表 
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;

    #endif

} OBJC2_UNAVAILABLE;

object發送消息時,Runtime庫會根據objectisa指針找到這個實例object所屬于的類,然后在類的方法列表以及父類方法列表尋找對應的方法運行,id是一個objc_object結構類型的指針,這個類型的對象能夠轉換成任何一種對象.
然后再來看看消息發送的函數:objc_msgSend函數,看起來objc_msgSend像是返回了數據,其實objc_msgSend從不返回數據而是你的方法被調用后返回了數據.下面詳細敘述下消息發送步驟:

  • 1)檢測這個 selector 是不是要忽略的,比如 Mac OS X 開發,有了垃圾回收就不理會 retain,release這些函數了,
  • 2)檢測這個target是不是 nil對象,Objc 的特性是允許對一個 nil對象執行任何一個方法不會 Crash,因為會被忽略掉.
  • 3)如果上面兩個都過了,那就開始查找這個類的 IMP,先從 cache里面找,完了找得到就跳到對應的函數去執行.
  • 4)如果 cache找不到就找一下方法分發表,如果分發表找不到就到超類的分發表去找,一直找,直到找到NSObject類為止,如果還找不到就要開始進入動態方法解析了.

參考文章:
刨根問底Objective-C Runtime


8.什么是 Method Swizzling ?####

Method Sswizzling指的是改變一個已存在的選擇器對應的實現過程,它依賴于Objective-C中方法的調用能夠在運行時改變,在運行時通過修改類的分發表dispatch tableselector對應的函數,來修改方法的實現.

  • 舉個例子,假設我們想跟蹤在一個iOS應用中每個視圖控制器展現給用戶的次數:

  • 我們可以給每個視圖控制器對應的viewWillAppear:實現方法中增加相應的跟蹤代碼,但是這樣做會產生大量重復的代碼;

  • 子類化可能是另一個選擇,但要求你將UIViewControllerUITableViewControllerUINavigationController 以及所有其他視圖控制器類都子類化,這也會導致代碼重復;

  • 幸好,還有另一個方法,在分類中進行method swizzling,在這之前先來看看選擇器、方法和實現的區別;

  • 選擇器、方法及實現:在Objective-C中,盡管這些詞經常被放在一起來描述消息傳遞的過程,但選擇器、方法及實現分別代表運行時的不同方面;下面是蘋果Objective-C Runtime Reference文檔中對它們的描述:

  • 選擇器typedef struct objc_selector *SEL):選擇器用于表示一個方法在運行時的名字,一個方法的選擇器是一個注冊到(或映射到)Objective-C運行時中的C字符串,它是由編譯器生成并在類加載的時候被運行時系統自動映射

  • 方法typedef struct objc_method *Method):一個代表類定義中一個方法的不明類型;

  • 實現typedef id (*IMP)(id, SEL, ...)):這種數據類型是實現某個方法的函數開始位置的指針,函數使用的是基于當前CPU架構的標準C調用規約,第一個參數是指向self的指針(也就是該類的某個實例的內存空間,或者對于類方法來說,是指向元類(metaclass)的指針),第二個參數是方法的選擇器,后面跟的都是參數;

理解這些概念之間關系最好的方式是:一個類Class維護一張調度表dispatch table,用于解析運行時發送的消息;調度表中的每個實體entry都是一個方法Method,其中key值是一個唯一的名字——選擇器SEL,它對應到一個實現IMP——實際上就是指向標準C函數的指針(注:個人覺得一個Method包含了方法名SEL和方法實現IMP,不知道這樣理解對不對

  • 在分類中進行Method Swizzing
#import <objc/runtime.h> 
 
@implementation UIViewController (Tracking) 
 
+ (void)load { 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        Class class = [self class]; 
 
        // When swizzling a class method, use the following: 
        // Class class = object_getClass((id)self); 
 
        SEL originalSelector = @selector(viewWillAppear:); 
        SEL swizzledSelector = @selector(xxx_viewWillAppear:); 
 
        Method originalMethod = class_getInstanceMethod(class, originalSelector); 
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 
 
        BOOL didAddMethod = 
            class_addMethod(class, 
                originalSelector, 
                method_getImplementation(swizzledMethod), 
                method_getTypeEncoding(swizzledMethod)); 
 
        if (didAddMethod) { 
            class_replaceMethod(class, 
                swizzledSelector, 
                method_getImplementation(originalMethod), 
                method_getTypeEncoding(originalMethod)); 
        } else { 
            method_exchangeImplementations(originalMethod, swizzledMethod); 
        } 
    }); 
} 
 
#pragma mark - Method Swizzling 
 
- (void)xxx_viewWillAppear:(BOOL)animated { 
    [self xxx_viewWillAppear:animated]; 
    NSLog(@"viewWillAppear: %@", self); 
} 
//這段代碼看起來像是會導致一個死循環,但其實并沒有,在Swizzling的過程中,xxx_viewWillAppear:會被重新分配給UIViewController的-viewWillAppear:的原始實現
//反而,如果我們在這個方法中調用viewWillAppear:才會真的導致死循環,因為這個方法的實現會在運行時被swizzle到viewWillAppear:的選擇器
@end 

現在,當UIViewController或它子類的任何實例觸發viewWillAppear:方法都會打印一條log日志;Method Swizzling就是改變類的調度表讓消息解析時從一個選擇器對應到另外一個的實現,同時將原始的方法實現混淆到一個新的選擇器;

  • 根據上述代碼總結Method Swizzling的實現過程
  • Swizzling應該在+load方法中實現:每個類的+load+initialize這兩個方法都會被Objective-C運行時系統自動調用,+load是在一個類最開始加載時調用,+initialize是在應用中第一次調用該類或它的實例的方法之前調用,這兩個方法都是可選的,只有實現了才會被執行;因為method swizzling會影響全局,所以減少冒險情況就很重要;+load能夠保證在類初始化的時候就會被加載,這為改變系統行為提供了一些統一性,但+initialize并不能保證在什么時候被調用——事實上也有可能永遠也不會被調用,例如應用程序從未直接的給該類發送消息
  • Swizzling應該在dispatch_once中實現:還是因為swizzling會改變全局,我們需要在運行時采取所有可用的防范措施,保障原子性就是一個措施,它確保代碼即使在多線程環境下也只會被執行一次,GCD中的diapatch_once就提供這些保障,它應該被當做swizzling的標準實踐

參考文章:
Objective-C Method Swizzling 的最佳實踐
Objective-C的hook方案(一): Method Swizzling
Method Swizzling


9. UIView 和 CALayer 是什么關系 ?####

首先應該明確:

  • CALayer繼承自NSObject,而UIView繼承自NSResponder,所以UIView是可以響應用戶的點擊事件的,而CALayer更多的是做渲染和動畫效果.
  • CALayerUIView都可以在屏幕上展示,每個在頁面上展示的UIView都是要在自己的根layer上進行對自己的繪制,就好比layer是畫板,而view是畫筆
  • 它們兩個在展示效率上并沒有很大差別,但是依然推薦子啊tableViewCell中使用drewRect:而不是subViews的形式進行cell繪制.

10.如何高性能的給 UIImageView 加個圓角 ?(不準說 layer.cornerRadius ).####

圓角是一種很常見的視圖效果,相比于直角它更加柔和優美,易于接受;當然,設置圓角會帶來一定的性能損耗,如何提高性能是另一個需要重點討論的話題.

UIImageView 的圓角通過直接截取圖片實現,其它視圖的圓角可以通過 Core Graphics 畫出圓角矩形實現,在下文會講到;

  • 如何設置圓角
    通常label.layer.cornerRadius = x;就可以設置圓角,但是cornerRadius只會影響視圖的背景顏色和border,對于內部還有子視圖的控件就會設置不成功,如UILabel;對于內部還有子視圖的控件還需要設置label.layer.masksToBounds = true;才能使cornerRadius屬性生效,此時會造成離屏渲染,設置圓角時masksToBounds的使用會降低手機每秒刷新的幀數,特別是在tableView中設置圓角時幀率下降非常快,幀率下降直接造成界面的不流暢;

Off-Screen Rending
離屏渲染,指的是圖形處理器GPU(Graphic Processing Unit)在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作,視圖和圓角的大小對幀率并沒有什么影響,設置圓角的數量才是影響幀率的原因;離屏渲染耗時是發生在離屏這個動作上面,而不是渲染;為什么離屏這個操作耗時?原因主要有緩沖區和上下文切換,創建新的緩沖區代價都不算大,付出最大代價的是上下文切換,不管實在GPU渲染過程中,還是進程切換,上下文切換都是一個相當耗時的操作;首先我要保存當前屏幕渲染環境,然后切換到一個新的繪制環境,申請繪制資源,初始化環境,然后開始一個繪制,繪制完畢后銷毀這個繪制環境,如需要切換到On-Screen Rendering或者再開始一個新的離屏渲染,重復之前的操作;

  • 針對上述設置圓角造成頻率下降的解決方案
    • 方案一:不要在滾動視圖使用cornerRadius或者mask,如果你非要作死使用怎么辦?那么這樣也可以拯救你:
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;

這樣大部分情況下可以馬上挽救你的幀數在55幀每秒以上,shouldRasterize = YES會使視圖渲染內容被緩存起來,下次繪制的時候可以直接顯示緩存,當然要在視圖內容不改變的情況下;參考文章iOS圖片圓角設置的正確姿勢

  • 方案二:預先生成圓角圖片并緩存起來,這個方法才是比較好的手段,預處理圓角圖片可以在后臺處理,處理完畢后緩存起來,再在主線程顯示,這就避免了不必要的離屏渲染了;
  • 方案三:在圖片上面覆蓋一個鏤空圓形圖片同樣可以實現圓形頭像效果,這個也是極為高效的方法,缺點就是對視圖的背景有要求,單色背景效果就最為理想;
  • 方案四:為普通的UIView 添加圓角和為UIImageView設置圓角的原理截然不同;
    • 為普通的 UIView 添加圓角的原理是手動畫出圓角,利用Core Graphics 自己畫出一個圓角矩形,再從當前的繪圖上下文中獲取圖片并返回,有了這個圖片后,我們創建一個UIImageView 并插入到視圖層級的底部(參考文章iOS 高效添加圓角效果實戰講解

    • UIImageView添加圓角通過直接截取圖片實現,可以給UIImage添加一個分類UIImage+Extension,分類中增加一個返回圓形圖片的方法,擴展性強(參考文章iOS開發中設置圓角的幾種方法

      #import <UIKit/UIKit.h>
      @interface UIImage (Extension)
      - (UIImage *)circleImage;
      @end
      
      #import "UIImage+Extension.h"
      @implementation UIImage (Extension)
       - (UIImage *)circleImage { 
         // 開始圖形上下文  
         UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); 
         // 獲得圖形上下文 
         CGContextRef ctx = UIGraphicsGetCurrentContext(); 
         // 設置一個范圍 
         CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); 
         // 根據一個rect創建一個橢圓
         CGContextAddEllipseInRect(ctx, rect); 
         // 裁剪 
         CGContextClip(ctx); 
         // 將原照片畫到圖形上下文 
         [self drawInRect:rect]; 
         // 從上下文上獲取剪裁后的照片 
         UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
         // 關閉上下文 
         UIGraphicsEndImageContext(); 
         return newImage;
      

}
```

 具體使用:
 ```

// 獲得的就是一個圓形的圖片
UIImage *placeHolder = [[UIImage imageNamed:@"defaultUserIcon"] circleImage];
```

參考文章:


圓角卡頓刨根問底
iOS 高效添加圓角效果實戰講解
iOS view圓角化的四種方法
開源高性能處理圓角
ZYCornerRadius 一句代碼,圓角風雨無阻


11.使用 drawRect 有什么影響 ?(可深可淺,至少得用過)####

drawRect方法依賴Core Graphics框架來進行自定義的繪制,但這種方法主要的缺點就是它處理touch事件的方式:每次按鈕被點擊后,都會用setNeddsDisplay進行強制重繪;而且不止一次,每次單點事件觸發兩次執行,這樣的話從性能的角度來說,對CPU和內存來說都是欠佳的;特別是如果在我們的界面上有多個這樣的UIButton實例.


參考文章:
內存惡鬼drawRect
iOS重繪機制drawRect
UIView的layoutSubviews和drawRect

12.ASIHttpRequest 或者 SDWebImage 里面給 UIImageView 加載圖片的邏輯是什么樣的 ?(把 UIImageView 放到 UITableViewCell 里面問更贊)####

13.麻煩你設計一個簡單的圖片內存緩存器( 移除策略是一定要說的 ).####

14.講講你用 Instrument 優化動畫性能的經歷吧(別問我什么是 Instrument )####

15. loadView 是干嘛用的 ?####

當你訪問一個ViewControllerview屬性時,如果此時view的值是nil,那么,ViewController就會自動調用loadView這個方法;這個方法就會加載或者創建一個view對象,賦值給view屬性;

loadView默認做的事情是:如果此ViewController存在一個對應的nib文件,那么就加載這個nib;否則,就創建一個UIView對象;

如果你用Interface Builder來創建界面,那么不應該重載這個方法;

如果你想自己創建view對象,那么可以重載這個方法;此時你需要自己給view屬性賦值;你自定義的方法不應該調用super;如果你需要對view做一些其他的定制操作,在viewDidLoad里面去做;

  • 根據上面的文檔可以知道,有兩種情況:

  • 1、如果你用了nib文件,重載這個方法就沒有太大意義;因為loadView的作用就是加載nib;如果你重載了這個方法不調用super,那么nib文件就不會被加載;如果調用了super,那么view已經加載完了,你需要做的其他事情在viewDidLoad里面做更合適;

  • 2、如果你沒有用nib,這個方法默認就是創建一個空的view對象;如果你想自己控制view對象的創建,例如創建一個特殊尺寸的view,那么可以重載這個方法,自己創建一個UIView對象,然后指定 self.view = myView; 但這種情況也沒有必要調用super,因為反正你也不需要在super方法里面創建的view對象;如果調用了super,那么就是浪費了一些資源而已;

16. viewWillLayoutSubView 講一講####

  • frame改變之前會調用viewWillLayoutSubviews,如橫豎屏切換的時候,系統會響應一些函數,其中 包括viewWillLayoutSubviewsviewDidLayoutSubviews;
  • ARC環境下單個viewController的生命周期:
  • initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib
  • loadView:加載view
  • viewDidLoad: view加載完畢
  • viewWillAppear:控制器的view將要顯示
  • viewWillLayoutSubviews:控制器的view將要布局子控件
  • viewDidLayoutSubviews:控制器的view布局子控件完成
    注:這期間系統可能會多次調用viewWillLayoutSubviews和viewDidLayoutSubviews這兩個方法
  • viewDidDisappear:控制器的view完全消失的時候

17. GCD 里邊有哪幾種 Queue ? 你自己建立過串行 Queue 嗎?背后的線程模型是什么樣的 ?####

18.用過 Coredata 或者 sqlite 嗎 ?讀寫是分線程的嗎 ?遇到過死鎖嗎 ?咋解決的 ?####

19. Http 的 post 和 get 的區別 ?(挺多的)####

20. 什么是 BST ?算法的復雜度是多少 ?####

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容