1. 簡述一下iOS的內(nèi)存管理?
內(nèi)存條中主要分為幾大類:棧區(qū)(stack)、堆區(qū)(heap)、常量區(qū)、代碼區(qū)(.text)、保留區(qū)。常量區(qū)分為未初始化區(qū)域(.bss)和已初始化區(qū)域(.data),棧區(qū)stack存儲順序是由高地址存向低地址,而堆區(qū)是由低地址向高地址存儲。內(nèi)存條中地址由低到高的區(qū)域分別為:保留區(qū),代碼區(qū),已初始化區(qū)(.data),未初始化區(qū)(.bss),堆區(qū)(heap),棧區(qū)(stack),內(nèi)核區(qū)。而程序員操作的主要是棧區(qū)與堆區(qū)還有常量區(qū)
當創(chuàng)建一個對象的實例并在堆上申請內(nèi)存時,對象的引用計數(shù)就為1,在其他對象中需要持有這個對象時,就需要把該對象的引用計數(shù)加1,需要釋放一個對象時,就將該對象的引用計數(shù)減1,直至對象的引用計數(shù)為0,對象的內(nèi)存會被立刻釋放。
MRC(手動管理引用計數(shù)):在MRC中增加的引用計數(shù)都是需要自己手動釋放的,遵循4個法則:
自己生成的對象,自己持有。
非自己生成的對象,自己也能持有。
不在需要自己持有對象的時候,釋放。
非自己持有的對象無需釋放。
ARC(自動管理引用計數(shù)):? 現(xiàn)在的iOS開發(fā)基本都是基于ARC的,所以開發(fā)人員大部分情況都是不需要考慮內(nèi)存管理的,因為編譯器已經(jīng)幫你做了。為什么說是大部分呢,因為底層的?Core Foundation?對象由于不在 ARC 的管理下,所以需要自己維護這些對象的引用計數(shù)。
2. 屬性關(guān)鍵字有哪些? block和delegate作為屬性用什么修飾 為什么?
關(guān)鍵字? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 注釋
readwrite? ? ? ? ? ? ? ? ? ?此標記說明屬性會被當成讀寫的,這也是默認屬性。
readonly? ? ? ? ? ? ? ? ? ? 此標記說明屬性只可以讀,也就是不能設(shè)置,可以獲取。
assign? ? ? ? ? ? ? ? ? ? ? ?不會使引用計數(shù)加1,也就是直接賦值。 基本數(shù)據(jù)類型和C數(shù)據(jù)類型默認屬性
retain? ? ? ? ? ? ? ? ? ? ? ? ?會使引用計數(shù)加1。
copy? ? ? ? ? ? ? ? ? ? ? ? ? ?建立一個索引計數(shù)為1的對象,在賦值時使用傳入值的一份拷貝。
nonatomic? ? ? ? ? ? ? ? ?非原子性訪問,多線程并發(fā)訪問會提高性能。
?atomic? ? ? ? ? ? ? ? ? ? ? ?原子性訪問。默認屬性
strong? ? ? ? ? ? ? ? ? ? ? ? 打開ARC時才會使用,相當于retain。默認屬性
weak? ? ? ? ? ? ? ? ? ? ? ? ? 打開ARC時才會使用,相當于assign,可以把對應(yīng)的指針變量置為nil
基本數(shù)據(jù):?atomic,readwrite,assign
普通的 OC 對象:?atomic,readwrite,strong
block在創(chuàng)建的時候,它的內(nèi)存是分配在棧(stack)上,而不是在堆(heap)上。他本身的作于域是屬于創(chuàng)建時候的作用域,一旦在創(chuàng)建時候的作用域外面調(diào)用block將導致程序崩潰(棧區(qū)上的過了作用域就清除了).
因為block變量默認是聲明為棧變量的,為了能夠在block的聲明域外使用,所以要把block拷貝(copy)到堆,所以說為了block屬性聲明和實際的操作一致,最好聲明為copy
使用copy修飾就會對Block的內(nèi)部對象進行強引用,導致循環(huán)引用。內(nèi)存無法釋放。就需要__weak對block內(nèi)部對象進行修飾
如果代理是strong修飾,那么當Viewcontroller需要釋放的時候—->就需要先釋放這個A—->A就需要釋放這個代理—–>代理又需要釋放這個Viewcontroller,就會引起循環(huán)引用。故而需要使用weak
3.? 什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?
在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決
assign自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak
自定義IBOutlet 控件屬性一般也使用weak;當然,也可以使用 strong,但是建議使用 weak
weak 策略在屬性所指的對象遭到摧毀時,系統(tǒng)會將 weak 修飾的屬性對象的指針指向 nil,在 OC 給 nil 發(fā)消息是不會有什么問題的;如果使用 assign 策略在屬性所指的對象遭到摧毀時,屬性對象指針還指向原來的對象,由于對象已經(jīng)被銷毀,這時候就產(chǎn)生了野指針,如果這時候在給此對象發(fā)送消息,很容造成程序奔潰assigin 可以用于修飾非 OC 對象,而 weak 必須用于 OC 對象。
4. 怎么用 copy 關(guān)鍵字?
NSString、NSArray、NSDictionary 等等經(jīng)常使用 copy 關(guān)鍵字,是因為他們有對應(yīng)的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary.
因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
為確保對象中的屬性值不會無意間變動,應(yīng)該在設(shè)置新屬性值時拷貝一份,保護其封裝性block,也經(jīng)常使用 copy,關(guān)鍵字block
使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).
在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的,但是建議寫上 copy,因為這樣顯示告知調(diào)用者“編譯器會自動對 block 進行了 copy 操作。
[不可變對象 copy]// 淺復制
[不可變對象 mutableCopy]//深復制
[可變對象 copy]//深復制?
[可變對象 mutableCopy]//深復制
5.?如何讓自定義類可以用 copy 修飾符?如何重寫帶 copy 關(guān)鍵字的 setter?
若想令自己所寫的對象具有拷貝功能,則需實現(xiàn) NSCopying 協(xié)議。如果自定義的對象分為可變版本與不可變版本,那么就要同時實現(xiàn) NSCopyiog 與NSMutableCopying 協(xié)議,不過一般沒什么必要,實現(xiàn) NSCopying 協(xié)議就夠了
// 實現(xiàn)不可變版本拷貝-(id)copyWithZone:(NSZone*)zone;// 實現(xiàn)可變版本拷貝-(id)mutableCopyWithZone:(NSZone*)zone;// 重寫帶 copy 關(guān)鍵字的 setter-(void)setName:(NSString*)name{_name=[name copy];}
6.. 簡述一下KVO和KVC的原理?具體到方法
KVC/KVO實現(xiàn)的根本是Objective-C的動態(tài)性和runtime
KVC:Key-Value Coding,即鍵值編碼 是一種不通過存取方法 而是通過屬性字符串間接訪問屬性的機制 。
-(id)valueForKey:(NSString*)key;
-(void)setValue:(id)value forKey:(NSString*)key;
-(id)valueForKeyPath:(NSString*)keyPath;
-(void)setValue:(id)value forKeyPath:(NSString*)keyPath;
前兩個方法無論獲取值還是賦值,只需要傳入屬性名稱的字符串就行了。但KVC也提供了傳入path的方法。所謂path,就是用點號連接的多層級的屬性,比如student.name,student屬性里的name屬性。
KVC的方法查找順序:
①檢查是否存在-<key>、-is<key>(只針對布爾值有效)或者-get<key>的訪問器方法,如果有可能,就是用這些方法返回值;
檢查是否存在名為-set<key>:的方法,并使用它做設(shè)置值。對于-get<key>和-set<key>:方法,將大寫Key字符串的第一個字母,并與Cocoa的方法命名保持一致;
②如果上述方法不可用,則檢查名為-_<key>、-_is<key>(只針對布爾值有效)、-_get<key>和-_set<key>:方法;
③如果沒有找到訪問器方法,可以嘗試直接訪問實例變量。實例變量可以是名為:<key>或_<key>;
④如果仍為找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。這些方法的默認實現(xiàn)都是拋出異常,我們可以根據(jù)需要重寫它們。
KVO:key-value observing 即鍵值觀察 提供了一種當前對象屬性被修改的時候通過當前對象的機制,KVO很適合實現(xiàn)model和controller之間的通訊。
當某個類的對象第一次被觀察時,系統(tǒng)就會在運行期動態(tài)地創(chuàng)建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。
派生類在被重寫的 setter 方法實現(xiàn)真正的通知機制
新類會重寫對應(yīng)的set方法,是為了在set方法中增加另外兩個方法的調(diào)用:
- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key
其中,didChangeValueForKey:方法負責調(diào)用:
- (void)observeValueForKeyPath:(NSString *)keyPath? ?ofObject:(id)object? ?change:(NSDictionary *)change context:(void *)context
KVO的缺點:
KVO的回調(diào)機制不能傳一個Selector或block作為回調(diào) 必須回調(diào)-addObserver:forKeyPath:options:context:方法
具體方法:
首先給目標對象的屬性添加觀察:-(void)addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context;
實現(xiàn)下面方法來接收通知,需要注意各個參數(shù)的含義:
-(void)observeValueForKeyPath:(nullable NSString*)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*,id>*)change context:(nullablevoid*)context;
最后要移除觀察者:
-(void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath;
7. setvalue forkey? 和 setobject forkey的區(qū)別?
?setValue:forKey: 的value是可以為nil的(但是當value為nil的時候,會自動調(diào)用removeObject:forKey方法);
setObject:forKey: 的value則不可以為nil。
setValue:forKey: 的key必須是不為nil的字符串類型;
setObject:forKey: 的key可以是不為nil的所有類型。
8. iOS的數(shù)據(jù)本地存儲有方式哪些?
iOS本地持久化存儲的路徑:
Documents: 最常用的目錄,存放重要的數(shù)據(jù),iTunes同步時會備份該目錄
Library/Caches: 一般存放體積大,不重要的數(shù)據(jù),iTunes同步時不會備份該目錄
Library/Preferences: 存放用戶的偏好設(shè)置,iTunes同步時會備份該目錄
tmp: 用于存放臨時文件,在程序未運行時可能會刪除該文件夾中的數(shù)據(jù),iTunes同步時不會備份該目錄
存儲方式:NSUserDefaults、Plist、NSKeyedArchiver、SQLite3、Core Data、Keychain、FMDB
NSUserDafaults存儲:
寫入:NSUserDefaults*login=[NSUserDefaults standardUserDefaults];
login setObject:self.passwordField.text forKey:@"token"]
;[login synchronize];
取出:NSUserDefaults*login=[NSUserDefaults standardUserDefaults];
NSString*str=[login objectForKey:@"token"];
特點:只能存儲OC常用數(shù)據(jù)類型(NSString、NSDictionary、NSArray、NSData、NSNumber等類型)而不能直接存儲自定義數(shù)據(jù)。
鍵值對存儲,直接指定存儲類型。
plist方式存儲:
重要方法: NSString*path=[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)objectAtIndex:0]stringByAppendingPathComponent:@"password.plist"];
特點:
只能存儲OC常用數(shù)據(jù)類型(NSString、NSDictionary、NSArray、NSData、NSNumber等類型)而不能直接存儲自定義模型對象。
plist文件存儲的位置,但一般存在Documents中
如果存儲圖片路徑的話,一定要存儲相對位置,因為每次啟動APP,plist文件的路徑就會變化,自然圖片的位置也就變化了。
NSKeyedArchiver歸檔(NSCoding)方式存儲:
-(nullable instancetype)initWithCoder:(nonnull NSCoder*)aDecoder{
-(void)encodeWithCoder:(nonnull NSCoder*)aCoder{
FMDB方式存儲:
NSString*docuPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)[0];
NSString*dbPath=[docuPath stringByAppendingPathComponent:@"test.db"];
NSLog(@"!!!dbPath = %@",dbPath);//2.創(chuàng)建對應(yīng)路徑下數(shù)據(jù)庫
db=[FMDatabase databaseWithPath:dbPath];
FMDatabase:一個FMDatabase對象代表一個單獨的SQLite數(shù)據(jù)庫,通過SQLite語句執(zhí)行數(shù)據(jù)庫的增刪改查操作
FMResultSet:使用FMDatabase對象查詢數(shù)據(jù)庫后的結(jié)果集
FMDatabaseQueue:用于多線程操作數(shù)據(jù)庫,它保證線程安全
開啟事務(wù) :beginTransaction
回滾事務(wù):rollback
提交事務(wù):commit
// 插入數(shù)據(jù)-(void)insertDataWithSQL:(NSString*)sql{[self.queue inDatabase:^(FMDatabase*db){BOOL result=[db executeUpdate:sql withArgumentsInArray:nil];if(result){NSLog(@"插入數(shù)據(jù)成功");}else{NSLog(@"插入數(shù)據(jù)失敗");}}];}
10. iOS多線程有哪些?GCD項目中有什么地方應(yīng)用到?線程死鎖有哪些原因?怎么避免?
NSThread:
NSThread是封裝程度最小最輕量級的,使用更靈活,但要手動管理線程的生命周期、線程同步和線程加鎖等,開銷較大;
兩種創(chuàng)建方式:
NSThread*newThread=[[NSThread alloc]initWithTarget:selfselector:@selector(run)object:nil];
/* 開啟線程 */[newThread start];
[NSThread detachNewThreadSelector:@selector(run)toTarget:selfwithObject:nil];[NSThread detachNewThreadWithBlock:^{NSLog(@"block run...");}];
NSOperation是基于GCD的一個抽象基類,將線程封裝成要執(zhí)行的操作,不需要管理線程的生命周期和同步,但比GCD可控性
更強,例如可以加入操作依賴(addDependency)、設(shè)置操作隊列最大可并發(fā)執(zhí)行的操作個數(shù)
(setMaxConcurrentOperationCount)、取消操作(cancel)等。
NSOperation作為抽象基類不具備封裝我們的操作的功能,需要使用兩個它的實體子類:NSBlockOperation和
NSInvocationOperation,或者繼承NSOperation自定義子類。
NSBlockOperation和NSInvocationOperation用法的主要區(qū)別是:前者執(zhí)行指定的方法,后者執(zhí)行代碼塊,相對來說后者更加靈活易用。
NSInvocationOperation*invoOpertion=[[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(run)object:nil];[invoOpertion start];/* NSBlockOperation初始化 */NSBlockOperation*blkOperation=[NSBlockOperation blockOperationWithBlock:^{NSLog(@"NSBlockOperation");}];[blkOperation start];
?GCD 中兩個核心概念:『任務(wù)』?和?『隊列』
任務(wù):就是執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼。在 GCD 中是放在 block 中的。執(zhí)行任務(wù)有兩種方
式:『同步執(zhí)行』和『異步執(zhí)行』。兩者的主要區(qū)別是:是否等待隊列的任務(wù)執(zhí)行結(jié)束,以及是否具備開啟新線程的能力。
可以使用?dispatch_queue_create?方法來創(chuàng)建隊列。該方法需要傳入兩個參數(shù):
第一個參數(shù)表示隊列的唯一標識符,用于 DEBUG,可為空。隊列的名稱推薦使用應(yīng)用程序 ID 這種逆序全程域名。
第二個參數(shù)用來識別是串行隊列還是并發(fā)隊列。DISPATCH_QUEUE_SERIAL?表示串行隊列,
DISPATCH_QUEUE_CONCURRENT?表示并發(fā)隊列
GCD 默認提供了:『主隊列(Main Dispatch Queue)』。所有放在主隊列中的任務(wù),都會放到主線程中執(zhí)行??墒?/p>
用?dispatch_get_main_queue()?方法獲得主隊列。
區(qū)別? ? ? ? ? ? ? ? ? ? ? ? ?并發(fā)隊? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 列串行隊列? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?主隊列
同步(sync)? ? 沒有開啟新線程,串行執(zhí)行任務(wù)? ? ? ?沒有開啟新線程,串行執(zhí)行任務(wù)? ? ? ? ? ? ?死鎖卡住不執(zhí)行
異步(async)? ?有開啟新線程,并發(fā)執(zhí)行任務(wù)? ? ? ? ? ?有開啟新線程(1條),串行執(zhí)行任務(wù)? 沒有開啟新線程,串行執(zhí)行任務(wù)
『主線程』中調(diào)用『主隊列』+『同步執(zhí)行』會導致死鎖問題。
這是因為主隊列中追加的同步任務(wù)和主線程本身的任務(wù)兩者之間相互等待,阻塞了『主隊列』,最終造成了主隊列所在的線程
(主線程)死鎖問題。
GCD 線程間的通信:
// 獲取主隊列 dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 回到主線程
? ? ? ? dispatch_async(mainQueue, ^{
? ? ? ? ? ? // 追加在主線程中執(zhí)行的任務(wù)
? ? ? ? ? ? [NSThread sleepForTimeInterval:2];? ? ? ? ? ? ? // 模擬耗時操作
? ? ? ? ? ? NSLog(@"2---%@",[NSThread currentThread]);? ? ? // 打印當前線程
? ? ? ? });
GCD 柵欄方法:dispatch_barrier_async
我們有時需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作。這樣我們就需要一個相當于柵欄一
樣的一個方法將兩組異步執(zhí)行的操作組給分割起來,當然這里的操作組里可以包含一個或多個任務(wù)。這就需要用到
dispatch_barrier_async方法在兩個操作組間形成柵欄。
dispatch_barrier_async(queue, ^{
? ? ? ? // 追加任務(wù) barrier
? ? ? ? [NSThread sleepForTimeInterval:2];? ? ? ? ? ? ? // 模擬耗時操作
? ? ? ? NSLog(@"barrier---%@",[NSThread currentThread]);// 打印當前線程
? ? });
GCD 延時執(zhí)行方法:dispatch_after
在指定時間(例如 3 秒)之后執(zhí)行某個任務(wù)。可以用 GCD 的dispatch_after?方法來實現(xiàn)。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
? ? ? ? // 2.0 秒后異步追加任務(wù)代碼到主隊列,并開始執(zhí)行
? ? ? ? NSLog(@"after---%@",[NSThread currentThread]);? // 打印當前線程
? ? });
GCD 一次性代碼(只執(zhí)行一次):dispatch_once
我們在創(chuàng)建單例、或者有整個程序運行過程中只執(zhí)行一次的代碼時,我們就用到了 GCD 的dispatch_once方法。使用
dispatch_once方法能保證某段代碼在程序運行過程中只被執(zhí)行 1 次,并且即使在多線程的環(huán)境下,dispatch_once也可以保證
線程安全。
/**
* 一次性代碼(只執(zhí)行一次)dispatch_once
*/
- (void)once {
? ? static dispatch_once_t onceToken;
? ? dispatch_once(&onceToken, ^{
? ? ? ? // 只執(zhí)行 1 次的代碼(這里面默認是線程安全的)
? ? });
}
GCD 快速迭代方法:dispatch_apply
通常我們會用 for 循環(huán)遍歷,但是 GCD 給我們提供了快速迭代的方法dispatch_apply。dispatch_apply按照指定的次數(shù)將指定
的任務(wù)追加到指定的隊列中,并等待全部隊列執(zhí)行結(jié)束。
我們可以利用并發(fā)隊列進行異步執(zhí)行。比如說遍歷
?0~5 這 6 個數(shù)字,for 循環(huán)的做法是每次取出一個元素,逐個遍歷。dispatch_apply?可以 在多個線程中同時(異步)遍歷多個
數(shù)字。
GCD 隊列組:dispatch_group
有時候我們會有這樣的需求:分別異步執(zhí)行2個耗時任務(wù),然后當2個耗時任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)。這時候我
們可以用到 GCD 的隊列組。
調(diào)用隊列組的dispatch_group_async先把任務(wù)放到隊列中,然后將隊列放入隊列組中。或者使用隊列組的
dispatch_group_enter、dispatch_group_leave組合來實現(xiàn)dispatch_group_async。
調(diào)用隊列組的?dispatch_group_notify?回到指定線程執(zhí)行任務(wù)。或者使用?dispatch_group_wait?回到當前線程繼續(xù)向下執(zhí)行(會
阻塞當前線程)。
// 等待上面的任務(wù)全部完成后,會往下繼續(xù)執(zhí)行(會阻塞當前線程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_enter?標志著一個任務(wù)追加到 group,執(zhí)行一次,相當于 group 中未執(zhí)行完畢任務(wù)數(shù) +1
dispatch_group_leave?標志著一個任務(wù)離開了 group,執(zhí)行一次,相當于 group 中未執(zhí)行完畢任務(wù)數(shù) -1。
當 group 中未執(zhí)行完畢任務(wù)數(shù)為0的時候,才會使?dispatch_group_wait?解除阻塞,以及執(zhí)行追加到?dispatch_group_notify?中
的任務(wù)。
GCD 信號量:dispatch_semaphore
dispatch_semaphore_create:創(chuàng)建一個 Semaphore 并初始化信號的總量
dispatch_semaphore_signal:發(fā)送一個信號,讓信號總量加 1
dispatch_semaphore_wait:可以使總信號量減 1,信號總量小于 0 時就會一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
Dispatch Semaphore 在實際開發(fā)中主要用于:保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
保證線程安全,為線程加鎖
我們在開發(fā)中,會遇到這樣的需求:異步執(zhí)行耗時任務(wù),并使用異步執(zhí)行的結(jié)果進行一些額外的操作。換句話說,相當于,將
將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)。比如說:AFNetworking 中 AFURLSessionManager.m 里面的tasksForKeyPath:方法。
通過引入信號量的方式,等待異步執(zhí)行任務(wù)結(jié)果,獲取到 tasks,然后再返回該 tasks。
11. Masony布局中使用了block?為什么沒有造成循環(huán)引用?
查看masonry源碼可以看到究竟:masonry中設(shè)置布局的方法中的block對象并沒有被View所引用,而是直接在方法內(nèi)部同步執(zhí)
行,執(zhí)行完以后block將釋放,其中捕捉的外部變量的引用計數(shù)也將還原到之前。
12. 在cell多列布局中,每列有多個lable,假如后臺數(shù)據(jù)返回的數(shù)量不一 ,怎么保證cell的高度是按最大lable布局?
13. 說一下iOS中主流的設(shè)計模式 ,
目前常用的幾種設(shè)計模式:代理模式、觀察者模式、MVC模式、單例模式、策略模式、工廠模式、MVVM
14. JSON解析的原理 ?
Runtime運行時機制 利用class_copyPropertyList??property_getName等方法
15. 簡述一下響應(yīng)者鏈?假如一個viewA 上有一個viewB,且viewB的面積大于viewA 點擊viewB且不和viewA重疊的區(qū)域 是否會響應(yīng) 為什么?
響應(yīng)者對象UIResponder,只有繼承UIResponder的的類,才能處理事件。
1.當iOS程序中發(fā)生觸摸事件后,系統(tǒng)會將事件加入到UIApplication管理的一個任務(wù)隊列中
2.UIApplication將處于任務(wù)隊列最前端的事件向下分發(fā)。即UIWindow。
3.UIWindow將事件向下分發(fā),即UIView。
4.UIView首先看自己是否能處理事件,觸摸點是否在自己身上。如果能,那么繼續(xù)尋找子視圖。
5.遍歷子控件,重復以上兩步。
6.如果沒有找到,那么自己就是事件處理者。如果
7.如果自己不能處理,那么不做任何處理。
其中 UIView不接受事件處理的情況主要有以下三種
1)alpha <0.01
2)userInteractionEnabled = NO
3.hidden = YES.
怎么尋找最合適的view:
此方法返回的View是本次點擊事件需要的最佳View
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
// 判斷一個點是否落在范圍內(nèi)
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
