iOS基礎(chǔ)知識
iOS的內(nèi)存管理
分為ARC、MRC兩種,通過引用計數(shù)來控制對象,引用計數(shù)為0的時候,釋放對象。
new、alloc、copy =1,retain +1,release -1。autorelease pool 是自動釋放池,池子里的對象會等到池子釋放時候一并釋放,只釋放計數(shù)為0的。
什么情況下會出現(xiàn)內(nèi)存的循環(huán)引用?
循環(huán)引用是只相互強(qiáng)引用導(dǎo)致無法釋放內(nèi)存,造成內(nèi)存泄露。
- Timer:timer一般為某個類的成員變量,創(chuàng)建timer的時候需要addTarget,這樣timer就強(qiáng)引用了類,timer處于validate狀態(tài)時,引用計數(shù)為1無法釋放,若不手動釋放,會出現(xiàn)循環(huán)引用;p.s.釋放要在dealloc之前釋放。
- block:block一般為某個類的成員變量,創(chuàng)建block的時候會對內(nèi)部對象強(qiáng)引用,造成循環(huán)引用;解決方法為,將block內(nèi)部的對象weak了再使用。如
__weak __typeof(&*self)weakself = self;。 - delegate:delegate一般為某個類的成員變量,要用weak來修飾,否則容易出現(xiàn)循環(huán)引用。
block中的weak self,是任何時候都需要加的么?
在block中用到self時要加。
GCD的queue,main queue中執(zhí)行的代碼,一定是在main thread么?
queue不一定,main queue一定是main thread。
NSOperationQueue有哪些使用方式
- 添加operation
- 順序執(zhí)行operation(依賴、優(yōu)先級)
- 設(shè)置并發(fā)數(shù)
- 取消所有operation(
[queue cancel]) - 阻塞當(dāng)前線程,等待queue的所有操作執(zhí)行完畢 (
[queue waitUntilAllOperationsAreFinished]) - 暫停繼續(xù)queue
NSThread中的Runloop的作用,如何使用?
- 使程序一直運(yùn)行并接收用戶的輸入
- 決定程序在何時處理哪些事件
- 節(jié)省CPU時間(當(dāng)程序啟動后,什么都沒有執(zhí)行的話,就不用讓CPU來消耗資源來執(zhí)行,直接進(jìn)入睡眠狀態(tài))
每個線程(NSThread)對象中內(nèi)部都有一個run loop(NSRunLoop)對象用來循環(huán)處理輸入事件,處理的事件包括兩類,一是來自Input sources的異步事件,一是來自Timer sources的同步事件;
[1]run Loop在處理輸入事件時會產(chǎn)生通知,可以通過Core Foundation向線程中添加run-loop observers來監(jiān)聽特定事件,以在監(jiān)聽的事件發(fā)生時做附加的處理工作。 主線程的Run Loop會在App運(yùn)行時自動運(yùn)行,子線程中需要手動運(yùn)行。
Run Loop就是一個處理事件源的循環(huán),你可以控制這個Run Loop運(yùn)行多久,如果當(dāng)前沒有事件發(fā)生,Run Loop會讓這個線程進(jìn)入睡眠狀態(tài)(避免再浪費CPU時間),如果有事件發(fā)生,Run Loop就處理這個事件。
如果子線程進(jìn)入一個循環(huán)需要不斷處理一些事件,那么設(shè)置一個Run Loop是最好的處理方式,如果需要Timer,那么Run Loop就是必須的。
開發(fā)中遇到的需要使用Run Loop的情況有:
- 需要使用Port或者自定義Input Source與其他線程進(jìn)行通訊。
- 子線程中使用了定時器
- 使用任何performSelector*****到子線程中運(yùn)行方法
- 使用子線程去執(zhí)行周期性任務(wù)
- NSURLConnection在子線程中發(fā)起異步請求
.h文件中的變量,外部可以直接訪問么?(注意是變量,不是property)
不可以。訪問變量需要有g(shù)etter,直接訪問會崩潰。
講述一下runtime的概念,message send如果尋找不到相應(yīng)的對象,會如何進(jìn)行后續(xù)處理 ?
- 類:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
消息機(jī)制:
[receiver message]會轉(zhuǎn)化為objc_msgSend(receiver, selector)方法調(diào)用過程:
先去緩存中找,沒有去方法列表里找,再沒有去父類的方法列表里找,如果找到則調(diào)用,實在找不到,動態(tài)方法解析,最后消息轉(zhuǎn)發(fā)。動態(tài)方法解析:
resolveInstanceMethod:和resolveClassMethod:兩種。
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
- 消息轉(zhuǎn)發(fā):
- 確定消息要發(fā)到哪里
- 將所有的參數(shù)一起發(fā)過去
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}