目錄
- block原理及使用
- 多線程方式
- OC內存管理機制
- 代理,通知的區別及使用場景
- 宏
- const,const,extern,volatile等關鍵字
1.定義block
typedef void(^animations)(void);
typedef void(^completion)(BOOL finished);
2.做過的項目是否涉及網絡訪問功能,使用什么對象完成網絡功能?
ASIHTTPRequest與NSURLConnection
3.簡單介紹下NSURLConnection類
NSURLConnection主要用于網絡訪問
+ sendSynchronousRequest:returningResponse:error:
是同步訪問數據,即當前線程會阻塞,并等待request的返回的response
– initWithRequest:delegate:
使用的是異步加載,當其完成網絡訪問后,會通過delegate回到主線程,并其委托的對象。
4.多線程是什么
多線程是個復雜的概念,按字面意思是同步完成多項任務,提高了資源的使用效率,從硬件、操作系統、應用軟件不同的角度去看,多線程被賦予不同的內涵,對于硬件,現在市面上多數的CPU都是多核的,多核的CPU運算多線程更為出色;
- 從操作系統角度,是多任務,現在用的主流操作系統都是多任務的,可以一邊聽歌、一邊寫博客;
- 對于應用來說,多線程可以讓應用有更快的回應,可以在網絡下載時,同時響應用戶的觸摸操作。
- 在iOS應用中,對多線程最初的理解,就是并發,它的含義是原來先做燒水,再摘菜,再炒菜的工作,會變成燒水的同時去摘菜,最后去炒菜。
5.iOS 中的多線程
iOS中的多線程,是Cocoa框架下的多線程,通過Cocoa的封裝,可以讓我們更為方便的使用線程,做過C++的同學可能會對線程有更多的理解,比如線程的創立,信號量、共享變量有認識,Cocoa框架下會方便很多,它對線程做了封裝,有些封裝,可以讓我們創建的對象,本身便擁有線程,也就是線程的對象化抽象,從而減少我們的工程,提供程序的健壯性。
GCD是(Grand Central Dispatch)
從系統級別提供的一個易用地多線程類庫,具有運行時的特點,能充分利用多核心硬件。GCD的API接口為C語言的函數,函數參數中多數有Block,關于Block的使用參看這里,為我們提供強大的“接口”。NSOperation與Queue
NSOperation是一個抽象類,它封裝了線程的細節實現,我們可以通過子類化該對象,加上NSQueue來同面向對象的思維,管理多線程程序。NSThread
NSThread是一個控制線程執行的對象,它不如NSOperation抽象,通過它我們可以方便的得到一個線程,并控制它。但NSThread的線程之間的并發控制,是需要我們自己來控制的,可以通過NSCondition實現。其他多線程
在Cocoa的框架下,通知、Timer和異步函數等都有使用多線程。
6.在項目什么時候選擇使用GCD,什么時候選擇NSOperation?
NSOperation
項目中使用NSOperation的優點是NSOperation是對線程的高度抽象,在項目中使用它,會使項目的程序結構更好,子類化NSOperation的設計思路,是具有面向對象的優點(復用、封裝),使得實現是多線程支持,而接口簡單,建議在復雜項目中使用。GCD
項目中使用GCD的優點是GCD本身非常簡單、易用,對于不復雜的多線程操作,會節省代碼量,而Block參數的使用,會是代碼更為易讀,建議在簡單項目中使用。
7.什么是block
對于閉包(block),有很多定義,其中閉包就是能夠讀取其它函數內部變量的函數,這個定義即接近本質又較好理解。
8.block 實現原理
Objective-C是對C語言的擴展,block的實現是基于指針
和函數指針
。
從計算語言的發展,最早的goto,高級語言的指針,到面向對象語言的block,從機器的思維,一步步接近人的思維,以方便開發人員更為高效、直接的描述出現實的邏輯(需求)。
- 使用實例
cocoaTouch框架下動畫效果的Block的調用
typedef void(^didFinishBlock)(NSObject *obj); // 這就聲明了一個didFinishBlock類型的block
@property(nonatomic,copy)didFinishBlock finishBlock;
1聲明一個blokc對象,注意對象屬性設置為copy,接到block 參數時,便會自動復制一份。
2__block是一種特殊類型,
使用該關鍵字聲明的局部變量,可以被block所改變,并且其在原函數中的值會被改變。
9.關于block
面試時,面試官會先問一些,是否了解block,是否使用過block,這些問題相當于開場白,往往是下面一系列問題的開始,所以一定要如實根據自己的情況回答。
9.1使用block和使用delegate完成委托模式有什么優點?
首先要了解什么是委托模式,委托模式在iOS中大量應用,其在設計模式中是適配器模式中的對象適配器,Objective-C中使用id類型指向一切對象,使委托模式更為簡潔。了解委托模式的細節:
- iOS設計模式—-委托模式
- 使用block實現委托模式,其優點是回調的block代碼塊定義在委托對象函數內部,使代碼更為緊湊;
- 適配對象不再需要實現具體某個protocol,代碼更為簡潔。
9.2 多線程與block
- GCD與Block
使用 dispatch_async 系列方法,可以以指定的方式執行block
- GCD編程實例
dispatch_async的完整定義
void dispatch_async( dispatch_queue_t queue, dispatch_block_t block);
功能:在指定的隊列里提交一個異步執行的block,不阻塞當前線程
通過queue來控制block執行的線程。主線程執行前文定義的 finishBlock對象
10.談談Object-C的內存管理方式及過程?
當你使用new,alloc和copy方法創建一個對象時,該對象的保留計數器值為1.當你不再使用該對象時,你要負責向該對象發送一條release或autorelease消息.這樣,該對象將在使用壽命結束時被銷毀.
當你通過任何其他方法獲得一個對象時,則假設該對象的保留計數器值為1,而且已經被設置為自動釋放,你不需要執行任何操作來確保該對象被清理.如果你打算在一段時間內擁有該對象,則需要保留它并確保在操作完成時釋放它.
如果你保留了某個對象,你需要(最終)釋放或自動釋放該對象.必須保持retain方法和release方法的使用次數相等.
11.Object-C有私有方法嗎?私有變量呢?
- objective-c – 類里面的方法只有兩種, 靜態方法和實例方法.
- @private可以用來修飾私有變量
- 在Objective‐C中,所有實例變量默認都是私有的,所有實例方法默認都是公有的
12.Object-C有多繼承嗎?沒有的話用什么代替?cocoa 中所有的類都是NSObject 的子類
- 多繼承在這里是用protocol 委托代理 來實現的
- ood的多態特性 在 obj-c 中通過委托來實現.
13.內存管理 Autorelease、retain、copy、assign的set方法和含義?
- 1.你初始化(alloc/init)的對象,你需要釋放(release)它。例如:
NSMutableArray aArray = [[NSArray alloc] init];
[aArray release];
- 2.你retain或copy的,你需要釋放它。例如:
[aArray retain]
[aArray release];
- 3.被傳遞(assign)的對象,你需要斟酌的retain和release。例如:
obj2 = [[obj1 someMethod] autorelease];
對象2接收對象1的一個自動釋放的值,或傳遞一個基本數據類型(NSInteger,NSString)時:你或希望將對象2進行retain,以防止它在被使用之前就被自動釋放掉。但是在retain后,一定要在適當的時候進行釋放。
關于索引計數(Reference Counting)的問題
- retain值 = 索引計數(Reference Counting)
- NSArray對象會retain(retain值加一)任何數組中的對象。當NSArray被卸載(dealloc)的時候,所有數組中的對象會 被 執行一次釋放(retain值減一)。不僅僅是NSArray,任何收集類(Collection Classes)都執行類似操作。例如 NSDictionary,甚至UINavigationController。
- Alloc/init建立的對象,索引計數為1。無需將其再次retain。
- [NSArray array]和[NSDate date]等
方法
建立一個索引計數為1的對象,但是也是一個自動釋放對象。所以是本地臨時對象,那么無所謂了。如果是打算在全Class中使用的變量(iVar),則必須retain它。 - 缺省的類方法返回值都被執行了
自動釋放
方法。(*如上中的NSArray) - 在類中的卸載方法
dealloc
中,release所有未被平衡的NS對象。(所有未被autorelease,而retain值為1的
)
14.C和obj-c 如何混用
- 1.obj-c的編譯器處理后綴為m的文件時,可以識別obj-c和c的代碼,處理mm文件可以識別obj-c,c,c++代碼,但cpp文件必須只能用c/c++代碼,而且cpp文件include的頭文件中,也不能出現obj-c的代碼,因為cpp只是cpp
- 2.在mm文件中混用cpp直接使用即可,所以obj-c混cpp不是問題
- 3.在cpp中混用obj-c其實就是使用obj-c編寫的模塊是我們想要的。
如果模塊以類實現,那么要按照cpp class的標準寫類的定義,頭文件中不能出現obj-c的東西,包括#import cocoa的。實現文件中,即類的實現代碼中可以使用obj-c的東西,可以import,只是后綴是mm。
如果模塊以函數實現,那么頭文件要按c的格式聲明函數,實現文件中,c++函數內部可以用obj-c,但后綴還是mm或m。
總結:只要cpp文件和cpp include的文件中不包含obj-c的東西就可以用了,cpp混用obj-c的關鍵是使用接口,而不能直接使用 實現代 碼,實際上cpp混用的是obj-c編譯后的o文件,這個東西其實是無差別的,所以可以用。obj-c的編譯器支持cpp
15.Objective-C堆和棧的區別?
管理方式:
對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產生memory leak。
申請大小:棧:
在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因 此,能從棧獲得的空間較小。堆:
堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。碎片問題:
對于堆來講,頻繁的new/delete勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個內存塊從棧中間彈出分配方式:
堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配由alloca函數進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。分配效率:
棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很復雜的。
16.什么時候用delegate,什么時候用Notification?
-
delegate
針對one-to-one關系,用于sender接受到reciever的某個功能反饋值。 -
notification
針對one-to-one/many/none,reciver,用于通知多個object某個事件。
17.用預處理指令#define聲明一個常數,用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
-
#define
語法的基本知識(例如:不能以分號結束,括號的使用,等等) - 懂得預處理器將為你計算常數表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
- 意識到這個表達式將使一個16位機的整型數溢出-因此要用到長整型符號
L
,告訴編譯器這個常數是的長整型數。 - 如果你在你的表達式中用到
UL(表示無符號長整型)
,那么你有了一個好的起點。記住,第一印象很重要。
18.寫一個”標準"宏MIN ,這個宏輸入兩個參數并返回較小的一個。
#define MIN(A,B) (A) <= (B) ? (A) : (B))
這個測試是為下面的目的而設的:
- 標識
#define
在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變為標準C的一部分,宏是方便產生嵌入代碼的唯一方
法, - 對于嵌入式系統來說,為了能達到要求的性能,嵌入代碼經常是必須的方法。
- 三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產生比 if-then-else 更優化的代碼,了解這個用法是很重要的。
懂得在宏中小心地把參數
用括號括起來
19.關鍵字const有什么含意?修飾類呢?static的作用,用于類呢?還有extern c的作用
const 意味著"只讀",下面的聲明都是什么意思?
1. const int a;
- a是一個常整型數。
2. int const a;
- a是一個常整型數。
3. const int *a;
- 意味著a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以)。
4. int * const a;
- 是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。
5.int const *a const;
- 意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。
結論:
1.關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數為常量是為了告訴了用戶這個參數的應用目的。
2.如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的) 通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。
使用技巧
- 欲阻止一個變量被改變,可以使用 const 關鍵字。在定義該 const 變量時,通常需要對它進行初始化,因為以后就沒有機會再去改變它了;
- 對指針來說,可以指定指針本身為 const,也可以指定指針所指的數據為 const,或二者同時指定為 const;
- 在一個函數聲明中,const 可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
- 對于類的成員函數,若指定其為 const 類型,則表明其是一個常函數,不能修改類的成員變量;
- 對于類的成員函數,有時候必須指定其返回值為 const 類型,以使得其返回值不為“左值”。
20.關鍵字volatile有什么含意?并給出三個不同的例子。
一個定義為 volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。
下面是volatile變量的幾個例子:
- 并行設備的硬件寄存器(如:狀態寄存器)
- 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
- 多線程應用中被幾個任務共享的變量
21.一個參數既可以是const還可以是volatile嗎? 一個指針可以是volatile 嗎?解釋為什么
1.是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2.是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
22.static 關鍵字的作用:
函數體內 static 變量的作用范圍為該函數體,不同于 auto 變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上次的值;
在模塊內的 static 全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
在模塊內的 static 函數只可被這一模塊內的其它函數調用,這個函數的使用范圍被限制在聲明它的模塊內;
在類中的 static 成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
在類中的 static 成員函數屬于整個類所擁有,這個函數不接收 this 指針,因而只能訪問類的static 成員變量。
23.線程與進程的區別和聯系?
進程和線程都是由操作系統所體會的程序運行的基本單元,系統利用該基本單元實現系統對應用的并發性
進程和線程的主要差別在于它們是不同的操作系統資源管理方式。
進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。
線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉。所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。
但對于一些要求同時進行并且又要共享某些變量的并發操作,只能用線程,不能用進程。
24.列舉幾種進程的同步機制,并比較其優缺點。
- 原子操作
- 信號量機制
- 自旋鎖
- 管程
- 會合
- 分布式系統
25.進程之間通信的途徑
共享存儲系統消息傳遞系統管道:以文件系統為基礎
26.進程死鎖的原因
資源競爭及進程推進順序非法
更多同類型文章請參考
iOS面試進階篇(一)
iOS面試進階篇(二)
iOS面試進階篇(三)
iOS面試進階篇(四)
iOS面試進階篇(五)
書寫整理多不容易,覺得寫的好的打賞一下吧。