由于每天接觸的東西很多,有些知識不記錄下來忘記的也快,等于做了無用功,受南峰子技術博客的啟發,準備把平常接觸到的iOS相關的知識點記錄下來,方便以后學習,溫故
本期主要記錄以下問題:
1. delegate 為啥要用 weak 修飾?
2. 什么情況使用 weak 關鍵字,相比 assign 有什么不同?
3. 怎么用 copy 關鍵字?
4. 這個寫法會出什么問題:@property (copy) NSMutableArray *array
5. 如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關鍵字的 setter?
6. @synthesize 和 @dynamic 分別有什么作用?
7. ARC下,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些?
8. 什么時候會報 unrecognized selector 的異常?
9. ?一個 OC 對象的 isa 的指針指向什么?有什么作用?
10. runtime 如何通過 selector 找到對應的 IMP 地址?
11. 使用 runtime Associate方法關聯的對象,需要在主對象dealloc的時候釋放么?
12. runloop 和線程有什么關系?
13. runloop 的 mode 作用是什么?
14. 猜想 runloop 內部是如何實現的?
15. 使用 block 時什么情況會發生引用循環,如何解決?
16. GCD的隊列(dispatch_queue_t)分哪兩種類型?
17. 如何用GCD同步若干個異步調用?(如根據若干個url異步加載多張圖片,然后在都下載完成后合成一張整圖)
18 .dispatch_barrier_async的作用是什么?
19. 蘋果為什么要廢棄dispatch_get_current_queue?
20. addObserver:forKeyPath:options:context:各個參數的作用分別是什么,observer中需要實現哪個方法才能獲得KVO回調?
21. 如何手動觸發一個value的KVO?
delegate 為啥要用 weak 修飾?
用 weak 表明該對象的銷毀由外部控制, 由于 delegate 的不確定性和非擁有性, 在 ARC 中應該首選 weak, ?因為 weak 可以防止野指針和循環引用的問題, 更加安全, 當 delegate 被銷毀的時候, deleagte 會被清空 (nill)
如果在iOS5.0下, 未支持 weak, 只能使用 unsafe_unretained 代替
代碼參考http://www.lxweimin.com/p/398472616435
補充: A 對象想要監聽 B 對象的事件可以設置 B 的 deleagte 為 A
什么情況使用 weak 關鍵字,相比 assign 有什么不同?
1. 在 ARC 有可能出現循環引用的情況, 比如 delegate
2. 自身已經對他進行了一次 strong 引用或者有父控件對他強引用, 比如 IBOutlet 控件屬性
和 assign 不同之處:
1. 和 assign 一樣在 setter 方法中既不會 release 舊值, 也不會 retain 新值, 但是 weak 修飾的對象銷毀時, 會清空該屬性對象避免野指針
2. assign 可以用在非 OC 對象, weak 只能用在 OC 對象?
怎么用 copy 關鍵字?
1. NSString, NSArray, NSDictionary 等經常使用 copy, 因為他們有對應的可變類型(避免多態造成運行時程序崩潰)
2. block 經常使用 copy, 這個是MRC遺留下來的傳統, 在MRC 中 block 是分配到棧中的,內存由系統管理, 使用 copy 可以把 block 拷貝一份到堆區, 由程序員管理, 在 ARC 中編譯器會對自動對 block 進行 copy 操作?
copy 也涉及淺拷貝和深拷貝的問題, 如果是對可變的變量進行 copy 操作是深拷貝, 會在內存中創建新的對象, 源對象的改變不會影響新的對象, 新的對象 retainCount 加 1, 源對象 retainCount 不變; 如果是對不可變的變量進行 copy 操作不會產生新的對象, 源對象引用計數加 1, 相當于 retain
NSMutableCopy 都是深拷貝操作
使用 copy 主要是向在內存中拷貝一個新的對象, 并且源對象改變以后不會影響新的對象
這個寫法會出什么問題:@property (copy) NSMutableArray *array
1. NSMutableArray 如果屬性是 copy 的話在.m生成的setter方法中會拷貝一個新的不可變的數組賦值給 NSMutableArray 的變量名, 實質是 NSArray, 由于NSArray是比不可變的, 用NSMutableArray的方法來動態修改新的對象在運行時會報錯
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x7ff240614b40
2. @property 應該加上 nonatomic, 默認是 atomic 多線程安全, 在生成的getter 和 setter器中會加同步鎖, 但是會影響性能?
如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關鍵字的 setter?
1. 聲明該類遵從 NSCopy 協議 (NSCoding 協議用在歸檔中)
2. 實現NSCopying協議的 copyWithZone 方法
- (id)copyWithZone:(NSZone *)zone {
? ?Student *student = [[[self Class] allocWithZone:zone] init];
? student.name = self.name;
? return student;
}
重寫帶 copy 關鍵字的 setter 器?
- (void)setName:(NSString *)name {
? ? if(_name != name) {
? ? ? ? ?[_name release];
? ? ? ? ?[_name copy];
? ? }
}
@synthesize 和 @dynamic 分別有什么作用?
如果 @synthesize和@dynamic都沒寫,那么默認的就是@syntheszie var = _var;?
另外@synthesize 和 @dynamic 是互斥的, 不能同時存在
@synthesize 如果沒有重寫 getter 和 setter 方法, 編譯器會加上這兩個方法和待下劃線的成員變量
@dynamic告訴編譯器屬性的 getter 和 setter 方法, 用戶自己實現, 不自動生成, 如果某個屬性聲明了 @dynamic 又沒有自己實現 getter 和 setter 方法, 編譯的時候 OK, 但是程序如果調用 getter 和 setter 方法就會導致程序崩潰, 編譯OK, 運行才執行相應的方法就是動態綁定
ARC下,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些?
OC對象: strong, atomic, readwrite
基本數據類型: assign, atomic, readwrite
什么時候會報 unrecognized selector 的異常?
OC 在向一個對象發送消息時, runtime 庫會根據對象的 isa 指針找到該對象實際所屬的類, 然后在該類及該類的父類方法列表尋找方法運行, 但是如果在最頂層的類中依然無法找到該方法, 程序運行時會拋此異常. 在程序崩潰之前 runtime 會給出第三次拯救程序的機會:
1. Method resolution(動態消息解析)
OC 運行時會調用 + resolveInstanceMethod: 或者 + resolveClassMethod: 讓你有機會添加一個函數. 如果添加了函數并返回 YES, ?runtime會重新啟動一次消息發送的過程, 如果resolve 方法返回NO, 運行時會移到下一步, 消息轉發(message forward)
PS: 可以利用消息轉發實現多繼承下面再說
2. Fast Forwarding
如果目標對象實現了- forwardingTargetForSelector: runtime 這時就會調用該方法, 給你把這個消息轉發給其他對象的機會, 只要這個方法返回的不是 nil (返回 self 會造成死循環) 整個消息發送的過程就會被重啟, 當然發送的對象會變成你返回的那個對象, 否則會進行 Normal Forwarding
這里不創建任何新的對象, 所以相對 Normal Forwarding(會創建 NSInvocation 對象)速度快
3. Normal Forwarding
這是 runtime 最后一次給你機會拯救程序的機會, 首先會發送 - methodSignatureForSelector: 獲得函數的參數和返回值類型, 如果該方法返回 nil, 程序這個時候就崩潰了(發送 - doesNotRecognizeSelector: 消息), 如果返回函數簽名, Runtime 就會創建一個 NSInvocation 對象, 并發送 - forwardInvocation: 消息給目標對象
一個 OC 對象的 isa 的指針指向什么?有什么作用?
指向他(對象)的類對象, 從而可以找到對象上的方法
"Objective-C 是一門面向對象的編程語言。每一個對象都是一個類的實例。在 Objective-C 語言的內部,每一個對象都有一個名為 isa 的指針,指向該對象的類。每一個類描述了一系列它的實例的特點,包括成員變量的列表,成員函數的列表等。每一個對象都可以接受消息,而對象能夠接收的消息列表是保存在它所對應的類中。"?
-- 來自唐巧博客http://blog.devtang.com/blog/2013/10/15/objective-c-object-model/
runtime 如何通過 selector 找到對應的 IMP 地址?
每一個類對象都有一個方法列表, 方法列表記錄著方法的名稱, 方法實現, 及方法的參數類型, selector的本質就是方法名, 通過這個方法名可以在對應的方法列表找到對應的方法實現
使用 runtime Associate 方法關聯的對象,需要在主對象 dealloc的時候釋放么?
ARC 不需要
MRC 在 retain 和 copy 策略下需要
runloop 和線程有什么關系?
runloop是為線程而生的, 每一個線程都會有一個對應的 runloop, 只不過主線程的 runloop 是默認開啟的(這就解釋了為什么應用會在無人操作的時候休息, 有人操作的時候工作), 可以通過 CFRunLoopRun 函數來開啟一個消息循環 (如果線程去執行一個長的已經確定的任務則不需要開啟消息循環)
SDWebImage有開起過子線程的消息循環
可以通過[NSRunLoop currentRunLoop]獲得當前線程的 RunLoop
每次消息循環開始時創建自動釋放池, 消息循環結束時釋放消息自動釋放池
runloop 的mode 作用是什么?
mode主要指定事件在循環中的優先級的, 蘋果公開的有兩個
NSDefaultRunLoopMode: 當把 NSTimer 以此模式添加到運行循環, 當用戶有事件處理的時候, NSTimer 不再被調度
?NSRunLoopCommonModes: 以此模式添加運行循環, 當用戶有事件處理, NSTimer 還能正常調度, 互不影響
猜想runloop內部是如何實現的?
runloop 是一個死循環, 從事件隊列取出事件, 執行相關代碼; 如過沒有事件, 則掛起, 等有事件了立即喚醒消息循環, 開始執行
使用block時什么情況會發生引用循環,如何解決?
一個對象強引用了 block, 在 block 中又使用了該對象就會造成循環引用, 解決辦法是將該對象使用__weak或者__block修飾符修飾之后再在block中使用。
__weak typeof(self) weakSelf =self;
GCD的隊列(dispatch_queue_t)分哪兩種類型?
1. 串行隊列 Serial Dispatch Queue
2. 并發隊列 Concurrent Dispatch Queue
如何用GCD同步若干個異步調用?(如根據若干個url異步加載多張圖片,然后在都下載完成后合成一張整圖)
1. 創建異步隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
2. 創建dispatch_group
dispatch_group_t group = dispatch_group_create();
3. 通過組來執行異步下載任務
dispatch_group_async(group, queue, ^{/*加載圖片1 */});
dispatch_group_async(group, queue, ^{/*加載圖片2 */});
dispatch_group_async(group, queue, ^{/*加載圖片3 */});
4. 任務完成之后合并圖片(等組里面所有的任務都執行完了會調下面這個方法)
dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 合并圖片});
dispatch_barrier_async的作用是什么?
是在并行隊列中, 等待前面操作并行任務執行完成后再執行dispatch_barrier_async中得任務, 如果后面還有并行任務會開始執行后續的并行任務
蘋果為什么要廢棄dispatch_get_current_queue?
容易造成死鎖
addObserver:forKeyPath:options:context:各個參數的作用分別是什么,observer中需要實現哪個方法才能獲得KVO回調?
觀察者, 觀察的屬性, 觀察的選項, 上下文
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context;
如何手動觸發一個 value 的 KVO
[self willChangeValueForKey:@"now"];“手動觸發self.now的KVO”
[self didChangeValueForKey:@"now"];“手動觸發self.now的KVO”
observeValueForKey:ofObject:change:context: