1. 程式人生 > >iOS面試問題全面梳理 --持續更新

iOS面試問題全面梳理 --持續更新

序言

目前,參加到iOS隊伍的人是越來越多,形勢不容樂觀。為iOS應聘者梳理一下面試題,希望能助一臂之力!

OC的理解與特性

  • OC作為一門面向物件的語言,自然具有面向物件的語言特性:封裝、繼承、多型。它既具有靜態語言的特性(如C++),又有動態語言的效率(動態繫結、動態載入等)。總體來講,OC確實是一門不錯的程式語言,

  • Objective-C具有相當多的動態特性,表現為三方面:動態型別(Dynamic typing)、動態繫結(Dynamic binding)和動態載入(Dynamic loading)。動態——必須到執行時(run time)才會做的一些事情。

  • 動態型別:即執行時再決定物件的型別,這種動態特性在日常的應用中非常常見,簡單來說就是id型別。事實上,由於靜態型別的固定性和可預知性,從而使用的更加廣泛。靜態型別是強型別,而動態型別屬於弱型別,執行時決定接受者。

  • 動態繫結:基於動態型別,在某個例項物件被確定後,其型別便被確定了,該物件對應的屬性和響應訊息也被完全確定。

  • 動態載入:根據需求載入所需要的資源,最基本就是不同機型的適配,例如,在Retina裝置上載入@2x的圖片,而在老一些的普通蘋裝置上載入原圖,讓程式在執行時新增程式碼模組以及其他資源,使用者可根據需要載入一些可執行程式碼和資源,而不是在啟動時就載入所有元件,可執行程式碼可以含有和程式執行時整合的新類。

簡述記憶體管理基本原則

  • 之前:OC記憶體管理遵循“誰建立,誰釋放,誰引用,誰管理”的機制,當建立或引用一個物件的時候,需要向她傳送alloc、copy、retain訊息,當釋放該物件時需要傳送release訊息,當物件引用計數為0時,系統將釋放該物件,這是OC的手動管理機制(MRC)。

  • 目前:iOS 5.0之後引用自動管理機制——自動引用計數(ARC),管理機制與手動機制一樣,只是不再需要呼叫retain、release、autorelease;它編譯時的特性,當你使用ARC時,在適當位置插入release和autorelease;它引用strong和weak關鍵字,strong修飾的指標變數指向物件時,當指標指向新值或者指標不復存在,相關聯的物件就會自動釋放,而weak修飾的指標變數指向物件,當物件的擁有者指向新值或者不存在時weak修飾的指標會自動置為nil。

  • 如果使用alloc、copy(mutableCopy)或者retian一個物件時,你就有義務,向它傳送一條release或者autorelease訊息。其他方法建立的物件,不需要由你來管理記憶體。

  • 向一個物件傳送一條autorelease訊息,這個物件並不會立即銷燬, 而是將這個物件放入了自動釋放池,待池子釋放時,它會向池中每一個物件傳送 一條release訊息,以此來釋放物件.

  • 向一個物件傳送release訊息,並不意味著這個物件被銷燬了,而是當這個物件的引用計數為0時,系統才會呼叫dealloc方法,釋放該物件和物件本身它所擁有的例項。

其他注意事項

  • 如果一個物件有一個_strong型別的指標指向著,找個物件就不會被釋放。如果一個指標指向超出了它的作用域,就會被指向nil。如果一個指標被指向nil,那麼它原來指向的物件就被釋放了。當一個檢視控制器被釋放時,它內部的全域性指標會被指向nil。用法“:不管全域性變數還是區域性變數用_strong描述就行。

  • 區域性變數:出了作用域,指標會被置為nil。

  • 方法內部建立物件,外部使用需要新增_autorelease;

  • 連線的時候,用_weak描述。

  • 代理使用unsafe_unretained就相當於assign;

  • block中為了避免迴圈引用問題,使用_weak描述;

  • 宣告屬性時,不要以new開頭。如果非要以new開頭命名屬性的名字,需要自己定製get方法名,如

@property(getter=theString) NSString * newString;
  • 如果要使用自動釋放池,用@autoreleasepool{}

  • ARC只能管理Foundation框架的變數,如果程式中把Foundation中的變數強制換成COre Foundation中的變數需要交換管理權;

  • 在非ARC工程中採用ARC去編譯某些類:-fobjc-arc。

  • 在ARC下的工程採用非ARC去編譯某些類:-fno-fobjc-arc。

如何理解MVC設計模式

MVC是一種架構模式,M表示MOdel,V表示檢視View,C表示控制器Controller:

  • Model負責儲存、定義、操作資料;

  • View用來展示書給使用者,和使用者進行操作互動;

  • Controller是Model和View的協調者,Controller把Model中的資料拿過來給View用。Controller可以直接與Model和View進行通訊,而View不能和Controller直接通訊。View與Controller通訊需要利用代理協議的方式,當有資料更新時,MOdel也要與Controller進行通訊,這個時候就要用Notification和KVO,這個方式就像一個廣播一樣,MOdel發訊號,Controller設定監聽接受訊號,當有資料更新時就發訊號給Controller,Model和View不能直接進行通訊,這樣會違背MVC設計模式。

如何理解MVVM設計模式

  • ViewModel層,就是View和Model層的粘合劑,他是一個放置使用者輸入驗證邏輯,檢視顯示邏輯,發起網路請求和其他各種各樣的程式碼的極好的地方。說白了,就是把原來ViewController層的業務邏輯和頁面邏輯等剝離出來放到ViewModel層。

  • View層,就是ViewController層,他的任務就是從ViewModel層獲取資料,然後顯示。

  • 如需瞭解更多,請檢視這篇文章

Objective-C 中是否支援垃圾回收機制?

  • OC是支援垃圾回收機制的(Garbage collection簡稱GC),但是apple的移動終端中,是不支援GC的,Mac桌面系統開發中是支援的.

  • 移動終端開發是支援ARC(Automatic Reference Counting的簡稱),ARC是在IOS5之後推出的新技術,它與GC的機制是不同的。我們在編寫程式碼時, 不需要向物件傳送release或者autorelease方法,也不可以呼叫delloc方法,編譯器會在合適的位置自動給使用者生成release訊息(autorelease),ARC 的特點是自動引用技術簡化了記憶體管理的難度.

協議的基本概念和協議中方法預設為什麼型別

OC中的協議是一個方法列表,且多少有點相關。它的特點是可以被任何類使用(實現),但它並不是類(這裡我們需要注意),自身不會實現這樣方法, 而是又其他人來實現協議經常用來實現委託物件(委託設計模式)。如果一個類採用了一個協議,那麼它必須實現協議中必須需要實現的方法,在協議中的方法預設是必須實現(@required),新增關鍵字@optional,表明一旦採用該協議,這些“可選”的方法是可以選擇不實現的。

簡述類目category優點和缺點

優點:
  • 不需要通過增加子類而增加現有類的行為(方法),且類目中的方法與原始類方法基本沒有區別;

  • 通過類目可以將龐大一個類的方法進行劃分,從而便於程式碼的日後的維護、更新以及提高程式碼的閱讀性;

    缺點:
  • 無法向類目新增例項變數,如果需要新增例項變數,只能通過定義子類的方式;

  • 類目中的方法與原始類以及父類方法相比具有更高優先順序,如果覆蓋父類的方法,可能導致super訊息的斷裂。因此,最好不要覆蓋原始類中的方法。

類別的作用

  • 給系統原有類新增方法,不能擴充套件屬性。如果類別中方法的名字跟系統的方法名一樣,在呼叫的時候類別中的方法優先順序更高;

  • 分散類的實現:如:

+ (NSIndexPath *)indexPathForRow:(NSInteger)row
inSection:(NSInteger)section
  • 原本屬於NSIndexPath的方法,但因為這個方法經常使用的表的時候呼叫、跟表的關係特別密切,因此把這個方法一類別的形式、宣告在UITableView.h中。

  • 宣告私有方法,某一個方法只實現,不宣告,相當於私有方法。

  • 類別不能宣告變數,類別不可以直接新增屬性。property描述setter方法,就不會報錯。

迴圈引用的產生原因,以及解決方法

  • 產生原因:如下圖所示,物件A和物件B相互引用了對方作為自己的成員變數,只有自己銷燬的時候才能將成員變數的引用計數減1。物件A的銷燬依賴於物件B的銷燬,同時物件B銷燬也依賴與物件A的銷燬,從而形成迴圈引用,此時,即使外界沒有任何指標訪問它,它也無法釋放。


迴圈引用示例圖


多個物件間依然會存在迴圈引用問題,形成一個環,在程式設計中,形成的環越大越不容易察覺,如下圖所示:

多個物件引用示例圖

解決方法:
  • 事先知道存在迴圈引用的地方,在合理的位置主動斷開一個引用,是物件回收;

  • 使用弱引用的方法。

鍵路徑(keyPath)、鍵值編碼(KVC)、鍵值觀察(KVO)

鍵路徑
  • 在一個給定的實體中,同一個屬性的所有值具有相同的資料型別。

  • 鍵-值編碼技術用於進行這樣的查詢—它是一種間接訪問物件屬性的機制。 - 鍵路徑是一個由用點作分隔符的鍵組成的字串,用於指定一個連線在一起的物件性質序列。第一個鍵的性質是由先前的性質決定的,接下來每個鍵的值也是相對於其前面的性質。

  • 鍵路徑使您可以以獨立於模型實現的方式指定相關物件的性質。通過鍵路徑,您可以指定物件圖中的一個任意深度的路徑,使其指向相關物件的特定屬性。

鍵值編碼KVC
  • 鍵值編碼是一種間接訪問物件的屬性使用字串來標識屬性,而不是通過呼叫存取方法,直接或通過例項變數訪問的機制,非物件型別的變數將被自動封裝或者解封成物件,很多情況下會簡化程式程式碼;

  • KVC的缺點:一旦使用 KVC 你的編譯器無法檢查出錯誤,即不會對設定的鍵、鍵路徑進行錯誤檢查,且執行效率要低於合成存取器方法和自定的 setter 和 getter 方法。因為使用 KVC 鍵值編碼,它必須先解析字串,然後在設定或者訪問物件的例項變數。

鍵值觀察KVO
  • 鍵值觀察機制是一種能使得物件獲取到其他物件屬性變化的通知 ,極大的簡化了程式碼。

  • 實現 KVO 鍵值觀察模式,被觀察的物件必須使用 KVC 鍵值編碼來修 改它的例項變數,這樣才能被觀察者觀察到。因此,KVC是KVO的基礎。

Demo

比如我自定義的一個button

[self addObserver:self forKeyPath:@"highlighted" options:0 context:nil]#pragma mark KVO 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
     if ([keyPath isEqualToString:@"highlighted"] ) { 
      [self setNeedsDisplay]; } 
  }

對於系統是根據keypath去取的到相應的值發生改變,理論上來說是和kvc機制的道理是一樣的。

KVC機制通過key找到value的原理
  • 當通過KVC呼叫物件時,比如:[self valueForKey:@”someKey”]時,程式會自動試圖通過下面幾種不同的方式解析這個呼叫。

  • 首先查詢物件是否帶有 someKey 這個方法,如果沒找到,會繼續查詢物件是否帶有someKey這個例項變數(iVar),如果還沒有找到,程式會繼續試圖呼叫 -(id) valueForUndefinedKey:這個方法。如果這個方法還是沒有被實現的話,程式會丟擲一個NSUndefinedKeyException異常錯誤。

  • 補充:KVC查詢方法的時候,不僅僅會查詢someKey這個方法,還會查詢getsomeKey這個方法,前面加一個get,或者_someKey以_getsomeKey這幾種形式。同時,查詢例項變數的時候也會不僅僅查詢someKey這個變數,也會查詢_someKey這個變數是否存在。

  • 設計valueForUndefinedKey:方法的主要目的是當你使用-(id)valueForKey方法從物件中請求值時,物件能夠在錯誤發生前,有最後的機會響應這個請求。

在 Objective-C 中如何實現 KVO

  • 註冊觀察者(注意:觀察者和被觀察者不會被保留也不會被釋放)

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 
options:(NSKeyValueObservingOptions)options 
context:(void *)context;
- (void)observeValueForKeyPath:(NSString *)keyPath 
ofObject:(id)object change:(NSDictionary *)change   context:(void *)context;
- (void)removeObserver:(NSObject *)observer 
forKeyPath:(NSString *)keyPath;
  • KVO中誰要監聽誰註冊,然後對響應進行處理,使得觀察者與被觀察者完全解耦。KVO只檢測類中的屬性,並且屬性名都是通過NSString來查詢,編譯器不會檢錯和補全,全部取決於自己。

代理的作用

  • 代理又叫委託,是一種設計模式,代理是物件與物件之間的通訊互動,代理解除了物件之間的耦合性。

  • 改變或傳遞控制鏈。允許一個類在某些特定時刻通知到其他類,而不需要獲取到那些類的指標。可以減少框架複雜度。

  • 另外一點,代理可以理解為java中的回撥監聽機制的一種類似。

  • 代理的屬性常是assign的原因:防止迴圈引用,以至物件無法得到正確的釋放。

NSNotification、Block、Delegate和KVO的區別

  • 代理是一種回撥機制,且是一對一的關係,通知是一對多的關係,一個對向所有的觀察者提供變更通知;

  • 效率:Delegate比NSNOtification高;

  • Delegate和Block一般是一對一的通訊;

  • Delegate需要定義協議方法,代理物件實現協議方法,並且需要建立代理關係才可以實現通訊;

  • Block:Block更加簡潔,不需要定義繁瑣的協議方法,但通訊事件比較多的話,建議使用Delegate;

Objective-C中可修改和不可以修改型別

  • 可修改不可修改的集合類,就是可動態新增修改和不可動態新增修改。

  • 比如NSArray和NSMutableArray,前者在初始化後的記憶體控制元件就是固定不可變的,後者可以新增等,可以動態申請新的記憶體空間

當我們呼叫一個靜態方法時,需要對物件進行 release 嗎?

  • 不需要,靜態方法(類方法)建立一個物件時,物件已被放入自動釋放池。在自動釋放池被釋放時,很有可能被銷燬。

當我們釋放我們的物件時,為什麼需要呼叫[super dealloc]方法,它的位置又是如何的呢?

  • 因為子類的某些例項是繼承自父類的,因此需要呼叫[super dealloc]方法, 來釋放父類擁有的例項,其實也就是子類本身的。一般來說我們優先釋放子類擁 有的例項,最後釋放父類所擁有的例項。

對謂詞的認識

  • Cocoa 中提供了一個NSPredicate的類,該類主要用於指定過濾器的條件, 每一個物件通過謂詞進行篩選,判斷條件是否匹配。如果需要了解使用方法,請看謂詞的具體使用

static、self、super關鍵字的作用

  • 函式體內static變數的作用範圍為該函式體,不同於auto變數,該變數的記憶體只被分配一次,因此其值在下次呼叫時仍維持上次的值.

  • 在模組內的 static 全域性變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問.

  • 在模組內的static函式只可被這一模組內的其它函式呼叫,這個函式的使用範圍被限制在宣告.

  • 在類中的static成員變數屬於整個類所擁有,對類的所有物件只有一份拷貝.

  • self:當前訊息的接收者。

  • super:向父類傳送訊息。

#include與#import的區別、#import 與@class 的區別

  • #include 和#import其效果相同,都是查詢類中定義的行為(方法);

  • #import不會引起交叉編譯,確保標頭檔案只會被匯入一次;

  • @class 的表明,只定 義了類的名稱,而具體類的行為是未知的,一般用於.h 檔案;

  • @class 比#import 編譯效率更高。

  • 此外@class 和#import 的主要區別在於解決引用死鎖的問題。

@public、@protected、@private 它們的含義與作用

  • @public:物件的例項變數的作用域在任意地方都可以被訪問 ;

  • @protected:物件的例項變數作用域在本類和子類都可以被訪問 ;

  • @private:例項變數的作用域只能在本類(自身)中訪問 .

解釋 id 型別

任意型別物件,程式執行時才決定物件的型別。

switch 語句 if 語句區別與聯絡

均表示條件的判斷,switch語句表示式只能處理的是整型、字元型和列舉型別,而選擇流程語句則沒有這樣的限制。但switch語句比選擇流程控制語句效率更高。

isMemberOfClass 和 isKindOfClass 聯絡與區別

  • 聯絡:兩者都能檢測一個物件是否是某個類的成員

  • 區別:isKindOfClass 不僅用來確定一個物件是否是一個類的成員,也可以用來確定一個物件是否派生自該類的類的成員 ,而isMemberOfClass 只能做到第一點。

  • 舉例:如 ClassA派 生 自NSObject 類 , ClassA *a = [ClassA alloc] init];,[a isKindOfClass:[NSObject class]] 可以檢查出 a 是否是 NSObject派生類 的成員,但 isMemberOfClass 做不到。

iOS 開發中資料永續性有哪幾種?

資料儲存的核心都是寫檔案。

  • 屬性列表:只有NSString、NSArray、NSDictionary、NSData可writeToFile;儲存依舊是plist檔案。plist檔案可以儲存的7中資料型別:array、dictionary、string、bool、data、date、number。

  • 物件序列化(物件歸檔):物件序列化通過序列化的形式,鍵值關係儲存到本地,轉化成二進位制流。通過runtime實現自動化歸檔/解檔,請參考這個文章。實現NSCoding協議必須實現的兩個方法:
    1.編碼(物件序列化):把不能直接儲存到plist檔案中得到資料,轉化為二進位制資料,NSData,可以儲存到本地;
    2.解碼(物件反序列化):把二進位制資料轉化為本來的型別。

  • SQLite 資料庫:大量有規律的資料使用資料庫。

  • CoreData :通過管理物件進行增、刪、查、改操作的。它不是一個數據庫,不僅可以使用SQLite資料庫來保持資料,也可以使用其他的方式來儲存資料。如:XML。

CoreData的介紹:

  • CoreData是面向物件的API,CoreData是iOS中非常重要的一項技術,幾乎在所有編寫的程式中,CoreData都作為資料儲存的基礎。

  • CoreData是蘋果官方提供的一套框架,用來解決與物件宣告週期管理、物件關係管理和持久化等方面相關的問題。

  • 大多數情況下,我們引用CoreData作為持久化資料的解決方案,並利用它作為持久化資料對映為記憶體物件。提供的是物件-關係對映功能,也就是說,CoreData可以將Objective-C物件轉換成資料,儲存到SQL中,然後將儲存後的資料還原成OC物件。

CoreData的特徵:

  • 通過CoreData管理應用程式的資料模型,可以極大程度減少需要編寫的程式碼數量。

  • 將物件資料儲存在SQLite資料庫已獲得性能優化。

  • 提供NSFetchResultsController類用於管理表檢視的資料,即將Core Data的持久化儲存在表檢視中,並對這些資料進行管理:增刪查改。

  • 管理undo/redo操縱;

  • 檢查託管物件的屬性值是否正確。

Core Data的6成員物件

  • 1.NSManageObject:被管理的資料記錄Managed Object Model是描述應用程式的資料模型,這個模型包含實體(Entity)、特性(Property)、讀取請求(Fetch Request)等。

  • 2.NSManageObjectContext:管理物件上下文,永續性儲存模型物件,參與資料物件進行各種操作的全過程,並監測資料物件的變化,以提供對undo/redo的支援及更新繫結到資料的UI。

  • 3.NSPersistentStoreCoordinator:連線資料庫的Persistent Store Coordinator相當於資料檔案管理器,處理底層的對資料檔案的讀取和寫入,一般我們與這個沒有交集。

  • 4.NSManagedObjectModel:被管理的資料模型、資料結構。

  • 5.NSFetchRequest:資料請求;

  • 6.NSEntityDescription:表格實體結構,還需知道.xcdatamodel檔案編譯後為.momd或者.mom檔案。

Core Data的功能

  • 對於KVC和KVO完整且自動化的支援,除了為屬性整合KVO和KVC訪問方法外,還整合了適當的集合訪問方法來處理多值關係;

  • 自動驗證屬性(property)值;

  • 支援跟蹤修改和撤銷操作;

  • 關係維護,Core Data管理資料的關係傳播,包括維護物件間的一致性;

  • 在記憶體上和介面上分組、過濾、組織資料;

  • 自動支援物件儲存在外部資料倉庫的功能;

  • 建立複雜請求:無需動手寫SQL語句,在獲取請求(fetch request)中關聯NSPredicate。NSPreadicate支援基本功能、相關子查詢和其他高階的SQL特性。它支援正確的Unicode編碼、區域感知查詢、排序和正則表示式;

  • 延遲操作:Core Data使用懶載入(lazy loading)方式減少記憶體負載,還支援部分實體化延遲載入和複製物件的資料共享機制;

  • 合併策略:Core Data內建版本跟蹤和樂觀鎖(optimistic locking)來支援多使用者寫入衝突的解決,其中,樂觀鎖就是對資料衝突進行檢測,若衝突就返回衝突的資訊;

  • 資料遷移:Core Data的Schema Migration工具可以簡化應對資料庫結構變化的任務,在某些情況允許你執行高效率的資料庫原地遷移工作;

  • 可選擇針對程式Controller層的整合,來支援UI的顯示同步Core Data在IPhone OS之上,提供NSFetchedResultsController物件來做相關工作,在Mac OS X上我們用Cocoa提供的繫結(Binding)機制來完成的。

物件可以被copy的條件

  • 只有實現了NSCopying和NSMutableCopying協議的類的物件才能被拷貝,分為不可變拷貝和可變拷貝,具體區別戳這

  • NSCopying協議方法為:

- (id)copyWithZone:(NSZone *)zone {
 MyObject *copy = [[[self class] allocWithZone: zone] init]; copy.username = [self.username copyWithZone:zone]; return copy;
}

自動釋放池工作原理

  • 自動釋放池是NSAutorelease類的一個例項,當向一個物件傳送autorelease訊息時,該物件會自動入池,待池銷燬時,將會向池中所有物件傳送一條release訊息,釋放物件。

  • [pool release]、 [pool drain]表示的是池本身不會銷燬,而是池子中的臨時物件都被髮送release,從而將物件銷燬。

在某個方法中 self.name = _name,name = _name 它 們有區別嗎,為什麼?

  • 前者是存在記憶體管理的setter方法賦值,它會對_name物件進行保留或者拷貝操作

  • 後者是普通賦值

  • 一般來說,在物件的方法裡成員變數和方法都是可以訪問的,我們通常會重寫Setter方法來執行某些額外的工作。比如說,外部傳一個模型過來,那麼我會直接重寫Setter方法,當模型傳過來時,也就是意味著資料發生了變化,那麼檢視也需要更新顯示,則在賦值新模型的同時也去重新整理UI。

解釋self = [super init]方法

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

定義屬性時,什麼時候用 assign、retain、copy 以及它們的之間的區別

  • assign:普通賦值,一般常用於基本資料型別,常見委託設計模式, 以此來防止迴圈引用。(我們稱之為弱引用).

  • retain:保留計數,獲得到了物件的所有權,引用計數在原有基礎上加1.

  • copy:一般認為,是在記憶體中重新開闢了一個新的記憶體空間,用來 儲存新的物件,和原來的物件是兩個不同的地址,引用計數分別為1。但是當copy物件為不可變物件時,那麼copy 的作用相當於retain。因為,這樣可以節約記憶體空間

堆和棧的區別

  • 棧區(stack)由編譯器自動分配釋放 ,存放方法(函式)的引數值, 區域性變數的值等,棧是向低地址擴充套件的資料結構,是一塊連續的記憶體的區域。即棧頂的地址和棧的最大容量是系統預先規定好的。

  • 堆區(heap)一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時由OS回收,向高地址擴充套件的資料結構,是不連續的記憶體區域,從而堆獲得的空間比較靈活。

  • 碎片問題:對於堆來講,頻繁的new/delete勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低。對於棧來講,則不會存在這個問題,因為棧是先進後出的佇列,他們是如此的一一對應,以至於永遠都不可能有一個記憶體塊從棧中間彈出.

  • 分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如區域性變數的分配。動態分配由alloca函式進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。

  • 分配效率:棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函式庫提供的,它的機制是很複雜的。

  • 全域性區(靜態區)(static),全域性變數和靜態變數的儲存是放在一塊 的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。程式結束後有系統釋放。

  • 文字常量區—常量字串就是放在這裡的。程式結束後由系統釋放。

  • 程式程式碼區—存放函式體的二進位制程式碼

怎樣使用performSelector傳入3個以上引數,其中一個為結構體

  • 因為系統提供的performSelector的API中,並沒有提供三個引數。因此,我們只能傳陣列或者字典,但是陣列或者字典只有存入物件型別,而結構體並不是物件型別,我們只能通過物件放入結構作為屬性來傳過去了.

 - (id)performSelector:(SEL)aSelector; 
 - (id)performSelector:(SEL)aSelector withObject:(id)object; 
 - (id)performSelector:(SEL)aSelector withObject:
    (id)object1 withObject:(id)object2;

具體實現如下:

typedef struct HYBStruct {
int a;
int b;
} *my_struct;
@interface HYBObject : NSObject
@property (nonatomic, assign) my_struct arg3;
@property (nonatomic, copy)  NSString *arg1;
@property (nonatomic, copy) NSString *arg2;

@end
@implementation HYBObject

// 在堆上分配的記憶體,我們要手動釋放掉- (void)dealloc {
free(self.arg3);

}@end

測試

my_struct str = (my_struct)(malloc(sizeof(my_struct)));
str->a = 1;
str->b = 2;
HYBObject *obj = [[HYBObject alloc] init];
obj.arg1 = @"arg1";
obj.arg2 = @"arg2";
obj.arg3 = str; 
[self performSelector:@selector(call:) withObject:obj]; 
// 在回撥時得到正確的資料的- (void)call:(HYBObject *)obj { NSLog(@"%d %d", obj.arg3->a, obj.arg3->b);
}

UITableViewCell上有個UILabel,顯示NSTimer實現的秒錶時間,手指滾動cell過程中,label是否重新整理,為什麼?

這是否重新整理取決於timer加入到Run Loop中的Mode是什麼。Mode主要是用來指定事件在執行迴圈中的優先順序的,分為:

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode):預設,空閒狀態

  • UITrackingRunLoopMode:ScrollView滑動時會切換到該Mode

  • UIInitializationRunLoopMode:run loop啟動時,會切換到該mode

  • NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
    蘋果公開提供的Mode有兩個:

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode)

  • NSRunLoopCommonModes(kCFRunLoopCommonModes)

  • 在程式設計中:如果我們把一個NSTimer物件以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)新增到主執行迴圈中的時候, ScrollView滾動過程中會因為mode的切換,而導致NSTimer將不再被排程。當我們滾動的時候,也希望不排程,那就應該使用預設模式。但是,如果希望在滾動時,定時器也要回調,那就應該使用common mode。

對於單元格重用的理解

  • 當螢幕上滑出螢幕時,系統會把這個單元格新增到重用佇列中,等待被重用,當有新單元從螢幕外滑入螢幕內時,從重用佇列中找看有沒有可以重用的單元格,若有,就直接用,沒有就重新建立一個。

解決cell重用的問題

  • UITableView通過重用單元格來達到節省記憶體的目的,通過為每個單元格指定一個重用標示(reuseidentifier),即指定了單元格的種類,以及當單元格滾出螢幕時,允許恢復單元格以便複用。對於不同種類的單元格使用不同的ID,對於簡單的表格,一個標示符就夠了。

  • 如一個TableView中有10個單元格,但螢幕最多顯示4個,實際上iPhone只為其分配4個單元格的記憶體,沒有分配10個,當滾動單元格時,螢幕內顯示的單元格重複使用這4個記憶體。實際上分配的cell的個數為螢幕最大顯示數,當有新的cell進入螢幕時,會隨機呼叫已經滾出螢幕的Cell所佔的記憶體,這就是Cell的重用。

  • 對於多變的自定義Cell,這種重用機制會導致內容出錯,為解決這種出錯的方法,把原來的

    UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:defineString]
    修改為:UITableViewCell *cell = [tableview cellForRowAtIndexPath:indexPath];

    這樣就解決掉cell重用機制導致的問題。

有a、b、c、d 4個非同步請求,如何判斷a、b、c、d都完成執行?如果需要a、b、c、d順序執行,該如何實現?

  • 對於這四個非同步請求,要判斷都執行完成最簡單的方式就是通過GCD的group來實現:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*任務a */ });
dispatch_group_async(group, queue, ^{ /*任務b */ });
dispatch_group_async(group, queue, ^{ /*任務c */ }); 
dispatch_group_async(group, queue, ^{ /*任務d */ }); 
dispatch_group_notify(group,dispatch_get_main_queue(), ^{ // 在a、b、c、d非同步執行完成後,會回撥這裡});<