第一篇:黑馬程序員:IOS面試寶典之第三方框架
黑馬程序員:IOS面試寶典之第三方框架
面試常問,你經(jīng)常使用一些第三方庫嗎?比如AFNetworking SDWebimage JsonModel等,有沒有去了解過里面的實(shí)現(xiàn)原理? 第三方框架
1.關(guān)于cocoaPods的問題:
1.1.使用過 CocoaPods 嗎?它是什么? CocoaPods的原理? CocoaPod是一個第三方庫的管理工具,用來管理項(xiàng)目中的第三方框架。
在終端中進(jìn)入(cd命令)你項(xiàng)目所在目錄,然后在當(dāng)前目錄下,利用vim創(chuàng)建Podfile,運(yùn)行: $ vim Podfile 然后在Podfile文件中輸入以下文字: platform :ios, '9.2'
pod “SDWebImage”, "~> 2.0”
然后保存退出。vim環(huán)境下,保存退出命令是 :wq
這時候,你會發(fā)現(xiàn)你的項(xiàng)目目錄中,出現(xiàn)一個名字為Podfile的文件,而且文件內(nèi)容就是你剛剛輸入的內(nèi)容。
這時候,你就可以利用CocoPods下載AFNetworking類庫了,運(yùn)行以下命令:$ pod install 1.2.用cocopod管理第三方框架的時候我想改版本,怎么辦到?
可以直接或者終端打開Podfile, 修改Podfile文件中第三方框架的版本
2.關(guān)于SDWebImage的問題: 2.1.SDWebImage的原理實(shí)現(xiàn)機(jī)制如何解決TableView卡的問題?
SDWebImage內(nèi)部實(shí)現(xiàn)過程
1> 入口 setImageWithURL:placeholderImage:options: 會先把 placeholderImage 顯示,然后 SDWebImageManager 根據(jù) URL 開始處理圖片。
2> 進(jìn)入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交給 SDImageCache 從緩存查找圖片是否已經(jīng)下載 queryDiskCacheForKey:delegate:userInfo:.3> 先從內(nèi)存圖片緩存查找是否有圖片,如果內(nèi)存中已經(jīng)有圖片緩存,SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4> SDWebImageManagerDelegate 回調(diào) webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示圖片。
5> 如果內(nèi)存緩存中沒有,生成 NSInvocationOperation 添加到隊(duì)列開始從硬盤查找圖片是否已經(jīng)緩存。
6> 根據(jù) URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進(jìn)行的操作,所以回主線程進(jìn)行結(jié)果回調(diào) notifyDelegate:。
7> 如果上一操作從硬盤讀取到了圖片,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過小,會先清空內(nèi)存緩存)。SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo:。進(jìn)而回調(diào)展示圖片。
8>如果從硬盤緩存目錄讀取不到圖片,說明所有緩存都不存在該圖片,需要下載圖片, 回調(diào) imageCache:didNotFindImageForKey:userInfo:。
9> 共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片。
10> 圖片下載由 NSURLConnection 來做,實(shí)現(xiàn)相關(guān) delegate 來判斷圖片下載中、下載完成和下載失敗。
11> connection:didReceiveData: 中利用 ImageIO 做了按圖片下載進(jìn)度加載效果。
12> connectionDidFinishLoading: 數(shù)據(jù)下載完成后交給 SDWebImageDecoder 做圖片解碼處理。
13> 圖片解碼處理在一個 NSOperationQueue 完成,不會拖慢主線程 UI。如果有需要對下載的圖片進(jìn)行二次處理,最好也在這里完成,效率會好很多。14> 在主線程 notifyDelegateOnMainThreadWithInfo:
宣告解碼完成,imageDecoder:didFinishDecodingImage:userInfo: 回調(diào)給 SDWebImageDownloader。15> imageDownloader:didFinishWithImage: 回調(diào)給 SDWebImageManager 告知圖片下載完成。
16> 通知所有的 downloadDelegates 下載完成,回調(diào)給需要的地方展示圖片。
17> 將圖片保存到 SDImageCache 中,內(nèi)存緩存和硬盤緩存同時保存。寫文件到硬盤也在以單獨(dú) NSInvocationOperation 完成,避免拖慢主線程。
18> SDImageCache 在初始化的時候會注冊一些消息通知,在內(nèi)存警告或退到后臺的時候清理內(nèi)存圖片緩存,應(yīng)用結(jié)束的時候清理過期圖片。
19> SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。20> SDWebImagePrefetcher 可以預(yù)先下載圖片,方便后續(xù)使用。如何解決tableView卡 , 通過設(shè)置最大并發(fā)數(shù), 設(shè)置當(dāng)前頁的cell, 而不是把所有cell一次性設(shè)置完, 以及數(shù)據(jù)圖片的三級緩存, 直接保存在內(nèi)存中和沙盒緩存中進(jìn)行讀取.降低網(wǎng)絡(luò)請求的次數(shù), 不僅節(jié)約用戶流量.也會保證tableView滑動的流暢性
2.2.SDWebImage怎樣實(shí)現(xiàn)圖片的緩存機(jī)制的?
圖片的緩存, 內(nèi)存緩存, 沙盒緩存, 操作緩存, 以tableViewController為例: 每次cell需要顯示,都需要重新調(diào)用-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }方法 每次調(diào)用tableView顯示行的數(shù)據(jù)源方法時, 如果需要從網(wǎng)絡(luò)加載圖片, 就需要將加載圖片這樣的耗時操作放在子線程上執(zhí)行, 從網(wǎng)絡(luò)上下載的圖片可以以鍵值對的形式保存在定義的可變字典中 ,將每張圖片的唯一的路徑作為鍵,將從網(wǎng)絡(luò)下載下來的圖片作為值, 保存在內(nèi)存緩存中, 這樣每次滑動tableView cell重用時就直接判斷內(nèi)存緩存中有沒有需要的圖片, 如果有就不需要再次下載,在沒有出現(xiàn)內(nèi)存警告或者程序員手動清理內(nèi)存緩存時, 就直接從內(nèi)存緩存中獲取圖片.為了每次退出程序,再次進(jìn)入程序時, 不浪費(fèi)用戶的流量, 需要將第一次進(jìn)入程序時加載的圖片保存在本地沙盒緩存文件中, 在沙盒中保存的圖片數(shù)據(jù)沒有被改變之前, 下次開啟程序就直接從沙盒的緩存文件中讀取需要顯示的圖片, 并將沙盒緩存文件夾(Cache)中保存的圖片保存到內(nèi)存緩存中, 這樣用戶每次滑動tableView cell重用時直接從內(nèi)存緩存中讀取而不是從沙盒中讀取, 節(jié)約時間.
第二篇:黑馬程序員:IOS面試寶典之關(guān)于動畫
關(guān)于動畫
1.談?wù)勀銓ore Graphic 繪圖的了解? CoreGraphics也稱為Quartz 2D 是UIKit下的主要繪圖系統(tǒng),頻繁的用于繪制自定義視圖。Core Graphics是高度集成于UIView和其他UIKit部分的。Core Graphics數(shù)據(jù)結(jié)構(gòu)和函數(shù)可以通過前綴CG來識別。
視圖可以通過子視圖、圖層或?qū)崿F(xiàn)drawRect:方法來表現(xiàn)內(nèi)容,如果說實(shí)現(xiàn)了drawRect:方法,那么最好就不要混用其他方法了,如圖層和子視圖。自定義繪圖大部分是由UIKit或者Core Graphics來實(shí)現(xiàn)的。
2D繪圖一般可以拆分成以下幾個操作: 線條 , 路徑 , 文本 , 圖片 , 漸變 由于像素是依賴于目標(biāo)的,所以2D繪圖并不能操作單獨(dú)的像素,我們可以從上下文(Context)讀取它。
繪圖就好比在畫布上拿著畫筆機(jī)械的進(jìn)行畫畫,通過制定不同的參數(shù)來進(jìn)行不同的繪制。
http://004km.cn/articles/jIJzMf
http://blog.csdn.net/mangosnow/article/details/37054765
2.Core Animation(核心動畫)? CoreAnimation也就是核心動畫, 是一組非常強(qiáng)大的動畫處理API, 可以使用少量的代碼做出絢麗的效果, 是直接作用在CALayer上的, 并非UIView, 并且Core Animation的動畫執(zhí)行過程都是在后臺操作, 不會阻塞主線程.所有動畫都是作用在CALayer上的, 當(dāng)把動畫添加到Layer上, 是不直接修改它的屬性, Core Animation維護(hù)了兩個平行l(wèi)ayer的層次結(jié)構(gòu), 模型層樹可以看到Layer的狀態(tài), 表示層樹則是動畫正在表現(xiàn)的值的近似.Core Animation的使用步驟: 1> 使用它需要先添加QuartzCore.framework框架和引入主頭文件
3>.通過調(diào)用CALayer的addAnimation:forKey:方法增加CAAnimation對象到CALayer中,這樣就能開始執(zhí)行動畫了
4>.通過調(diào)用CALayer的removeAnimationForKey:方法可以停止CALayer中的動畫
1.轉(zhuǎn)場動畫? CATransition-轉(zhuǎn)場動畫, 作為CAAnimation的子類,用于做轉(zhuǎn)場動畫,能夠?yàn)閷犹峁┮瞥銎聊缓鸵迫肫聊坏膭赢嬓ЧOS比Mac OS X的轉(zhuǎn)場動畫效果少一點(diǎn).UINavigationController就是通過CATransition實(shí)現(xiàn)了將控制器的視圖推入屏幕的動畫效果.如下是轉(zhuǎn)場動畫的過渡效果:
使用UIView的動畫函數(shù), 實(shí)現(xiàn)轉(zhuǎn)場動畫 1> 單視圖:
+(void)transitionWithView:(UIView*)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options
animations:(void(^)(void))animations completion:(void(^)(BOOL finished))completion;參數(shù)說明:
duration:動畫的持續(xù)時間 view:需要進(jìn)行轉(zhuǎn)場動畫的視圖 options:轉(zhuǎn)場動畫的類型
animations:將改變視圖屬性的代碼放在這個block中 completion:動畫結(jié)束后,會自動調(diào)用這個block 2> 雙視圖: +(void)transitionFromView:(UIView*)fromView toView:(UIView*)toView
duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options finished))completion;參數(shù)說明:
duration:動畫的持續(xù)時間 options:轉(zhuǎn)場動畫的類型
animations:將改變視圖屬性的代碼放在這個block中 completion:動畫結(jié)束后,會自動調(diào)用這個block
completion:(void
(^)(BOOL 2.一個動畫怎么實(shí)現(xiàn)? 以轉(zhuǎn)場動畫為例: 1> 創(chuàng)建CATransition對象
CATransition *animation = [CATransition animation];2> 設(shè)置運(yùn)動時間(即動畫時間)animation.duration = DURATION;3> 設(shè)置運(yùn)動type(類型)
animation.type = type;if(subtype!= nil){ 4> 設(shè)置子類(和type配合使用, 指定運(yùn)動的方向)animation.subtype = subtype;} 5> 設(shè)置運(yùn)動速度(動畫的運(yùn)動軌跡,用于變化起點(diǎn)和終點(diǎn)之間的插值計(jì)算,形象點(diǎn)說它決定了動畫運(yùn)行的節(jié)奏,比如是均勻變化(相同時間變化量相同)還是先快后慢,先慢后快還是先慢再快再慢)animation.timingFunction = UIViewAnimationOptionCurveEaseInOut;
6> 將動畫添加到view的Layer層
[view.layer addAnimation:animation forKey:@“animation”];動畫類型如下: typedef enum : NSUInteger { Fade = 1, //淡入淡出 Push, //推擠 Reveal, //揭開 MoveIn, //覆蓋 Cube, //立方體 SuckEffect, //吮吸 OglFlip, //翻轉(zhuǎn) RippleEffect, //波紋
PageCurl, //翻頁 PageUnCurl, //反翻頁 CameraIrisHollowOpen, //開鏡頭 CameraIrisHollowClose, //關(guān)鏡頭 CurlDown, //下翻頁 CurlUp, //上翻頁 FlipFromLeft, //左翻轉(zhuǎn) FlipFromRight, //右翻轉(zhuǎn) } AnimationType;3.CADisplayLink CADisplayLink是一種以屏幕刷新頻率觸發(fā)的時鐘機(jī)制,每秒鐘執(zhí)行大約60次左右
CADisplayLink是一個計(jì)時器,可以使繪圖代碼與視圖的刷新頻率保持同步,而NSTimer無法確保計(jì)時器實(shí)際被觸發(fā)的準(zhǔn)確時間 使用方法:
定義CADisplayLink并制定觸發(fā)調(diào)用方法 將顯示鏈接添加到主運(yùn)行循環(huán)隊(duì)列
第三篇:黑馬程序員:IOS面試寶典之OC語言
OC語言(★★)
有人可能會問對于我們學(xué)iOS的同學(xué)來講,面試還會問Objective-C基礎(chǔ)嗎?答案是會的,但是不會太多,因此我給了兩顆星的重要程度。一般筆試的時候出現(xiàn)Objective-C基礎(chǔ)題的概率比較大,口頭面試的時候比較少,比如自己在面試的時候一些對基礎(chǔ)知識比較看重的面試官會深究著Objective-C基礎(chǔ)去問,比如Objective-C內(nèi)存管理等等。
一、Objective-C語法
1.面向?qū)ο蠖加心男┨匦砸约澳銓@些特性的理解
繼承:繼承是從已有類得到繼承信息創(chuàng)建新類的過程。提供繼承信息的類被稱為父類(超類、基類);得到繼承信息的類被稱為子類(派生類)。繼承讓變化中的軟件系統(tǒng)有了一定的延續(xù)性,同時繼承也是封裝程序中可變因素的重要手段。
封裝:通常認(rèn)為封裝是把數(shù)據(jù)和操作數(shù)據(jù)的方法綁定起來,對數(shù)據(jù)的訪問只能通過已定義的接口。面向?qū)ο蟮谋举|(zhì)就是將現(xiàn)實(shí)世界描繪成一系列完全自治、封閉的對象。我們在類中編寫的方法就是對實(shí)現(xiàn)細(xì)節(jié)的一種封裝;我們編寫一個類就是對數(shù)據(jù)和數(shù)據(jù)操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口。
多態(tài)性:多態(tài)性是指允許不同子類型的對象對同一消息作出不同的響應(yīng)。簡單的說就是用同樣的對象引用調(diào)用同樣的方法但是做了不同的事情。多態(tài)性分為編譯時的多態(tài)性和運(yùn)行時的多態(tài)性。如果將對象的方法視為對象向外界提供的服務(wù),那么運(yùn)行時的多態(tài)性可以解釋為:當(dāng)A系統(tǒng)訪問B系統(tǒng)提供的服務(wù)時,B系統(tǒng)有多種提供服務(wù)的方式,但一切對A
系統(tǒng)來說都是透明的。方法重載(overload)實(shí)現(xiàn)的是編譯時的多態(tài)性(也稱為前綁定),而方法重寫(override)實(shí)現(xiàn)的是運(yùn)行時的多態(tài)性(也稱為后綁定)。運(yùn)行時的多態(tài)是面向?qū)ο笞罹璧臇|西,要實(shí)現(xiàn)多態(tài)需要做兩件事:1.方法重寫(子類繼承父類并重寫父類中已有的或抽象的方法);2.對象造型(用父類型引用引用子類型對象,這樣同樣的引用調(diào)用同樣的方法就會根據(jù)子類對象的不同而表現(xiàn)出不同的行為)。
抽象:抽象是將一類對象的共同特征總結(jié)出來構(gòu)造類的過程,包括數(shù)據(jù)抽象和行為抽象兩方面。抽象只關(guān)注對象有哪些屬性和行為,并不關(guān)注這些行為的細(xì)節(jié)是什么。
2.我們說的Objective-C是動態(tài)運(yùn)行時語言是什么意思?(When we call objective c is runtime language what does it mean?)主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運(yùn)行時。這個問題其實(shí)淺涉及到兩個概念,運(yùn)行時和多態(tài)。簡單來說, 運(yùn)行時機(jī)制使我們直到運(yùn)行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法。多態(tài):不同對象以自己的方式響應(yīng)相同的消息的能力叫做多態(tài)。意思就是假設(shè)生物類(life)都用有一個相同的方法-eat;那人類屬于生物,豬也屬于生物,都繼承了life后,實(shí)現(xiàn)各自的eat,但是調(diào)用是我們只需調(diào)用各自的eat方法。也就是不同的對象以自己的方式響應(yīng)了相同的消息(響應(yīng)了eat這個選擇器)。因此也可以說,運(yùn)行時機(jī)制是多態(tài)的基礎(chǔ).3.readwrite,readonly, assign,retain,copy,nonatomic, strong, weak屬性的作用?并區(qū)別strong(強(qiáng)引用)、weak(弱引用)?什么情況使用copy,assign和retain? readwrite 是可讀可寫特性;需要生成getter方法和setter方法時;
readonly 是只讀特性只會生成getter方法不會生成setter方法,不希望屬性在類外改變;
assign 是賦值特性,setter方法將傳入?yún)?shù)賦值給實(shí)例變量;僅設(shè)置變量時;, assign用于簡單數(shù)據(jù)類型,如NSInteger,double,bool;retain 表示持有特性,setter方法將傳入?yún)?shù)先保留,再賦值,傳入?yún)?shù)的retaincount會+1;
copy 表示賦值特性,setter方法將傳入對象復(fù)制一份;需要完全一份新的變量時;nonatomic 非原子操作,決定編譯器生成的setter getter是否是原子操作;atomic表示多線程安全,一般使用nonatomic。
retain 和copy用戶對象,copy用于當(dāng) a指向一個對象, b也想指向同樣的對象的時候,如果用assign,a如果釋放,再調(diào)用b會crash,如果用copy 的方式,a和b各自有自己的內(nèi)存,就可以解決這個問題。retain 會使計(jì)數(shù)器加1,也可以解決assign的問題。另外:atomic和nonatomic用來決定編譯器生成的getter和setter是否為原子操作。在多線程環(huán)境下,原子操作是必要的,否則有可能引起錯誤的結(jié)果。
其實(shí)從這個也可以引申到內(nèi)存管理的一系列問題.面試官會讓你講講MRC和ARC的理解,隨即也引申出Block的內(nèi)存機(jī)制,為啥推薦Block用copy,Block會存在哪些內(nèi)存問題?循環(huán)引用?
4.分別描述內(nèi)存管理要點(diǎn)、autorelease、release、NSAutoreleasePool?并說明autorelease是什么時候被release的?簡述什么時候由你負(fù)責(zé)釋放對象,什么時候不由你釋放?[NSAutoreleasePool release]和[NSAutoreleasePool drain]有什么區(qū)別? 內(nèi)存管理要點(diǎn): Objective-C 使用引用計(jì)數(shù)機(jī)制(retainCount)來管理內(nèi)存。內(nèi)存每被引用一次,該內(nèi)存的引用計(jì)數(shù)+1,每被釋放一次引用計(jì)數(shù)-1。當(dāng)引用計(jì)數(shù) = 0 的時候,調(diào)用該對象的 dealloc 方法,來徹底從內(nèi)存中刪除該對象。alloc,allocWithZone,new(帶初始化)時:該對象引用計(jì)數(shù) +1;
retain:手動為該對象引用計(jì)數(shù) +1;copy:對象引用計(jì)數(shù) +1;
mutableCopy:生成一個新對象,新對象引用計(jì)數(shù)為 1;release:手動為該對象引用計(jì)數(shù)-1;
autorelease:把該對象放入自動釋放池,當(dāng)自動釋放池釋放時,其內(nèi)的對象引用計(jì)數(shù)-1。
NSAutoreleasePool: NSAutoreleasePool是通過接收對象向它發(fā)送的autorelease消息,記錄該對象的release消息,當(dāng)自動釋放池被銷毀時,會自動向池中的對象發(fā)送release消息。autorelease 是在自動釋放池被銷毀,向池中的對象發(fā)送release只能釋放自己擁有的對象, 區(qū)別是:在引用計(jì)數(shù)環(huán)境下(在不使用ARC情況下),兩者基本一樣,在GC(垃圾回收制)環(huán)境下,release 是一個no-op(無效操作),所以無論是不是GC都使用drain
面試中內(nèi)存管理,release和autorelease的含義?這里尤其要強(qiáng)調(diào)下autorelease,它引申出自動釋放池,也能引申出Run loop!5.自動釋放池是什么,如何工作 ? 當(dāng) 您向一個對象發(fā)送一個autorelease消息時,Cocoa就會將該對象的一個引用放入到最新的自動釋放池。它仍然是個對象,因此自動釋放池定義的作用域內(nèi)的其它對象可以向它發(fā)送消息。當(dāng)程序執(zhí)行到作用域結(jié)束的位置時,自動釋放池就會被釋放,池中的所有對象也就被釋放。
1>.Objective-C 是 通過一種“referring counting”(引用計(jì)數(shù))的方式來管理內(nèi)存的, 對象在開始分配內(nèi)存(alloc)的時候引用計(jì)數(shù)為1,以后每當(dāng)碰到有copy,retain的時候引用計(jì)數(shù)都會加1, 每當(dāng)碰到release和autorelease的時候引用計(jì)數(shù)就會減1,如果此對象的計(jì)數(shù)變?yōu)榱?, 就會被系統(tǒng)銷毀.2>.NSAutoreleasePool 就是用來做引用計(jì)數(shù)的管理工作的,這個東西一般不用自己管理.3>.autorelease和release沒什么區(qū)別,只是引用計(jì)數(shù)減1的時機(jī)不同而已,autorelease會在對象的使用真正結(jié)束的時候才做引用計(jì)數(shù)減1.6.IPhone OS有沒有垃圾回收?autorelease 和垃圾回收制(gc)有什么關(guān)系? IPhone OS 中沒有垃圾回收。autorelease只是延遲釋放,gc是每隔一段時間詢問程序,看是否有無指針指向的對象,若有,就將它回收。他們兩者沒有什么關(guān)系。
7.簡述NotificationCenter、KVC、KVO、Delegate?并說明它們之間的區(qū)別? KVO(Key-Value-Observing):是鍵值監(jiān)聽,鍵值觀察機(jī)制,當(dāng)觀察者為一個對象的屬性進(jìn)行了注冊,被觀察對象的isa指針被修改的時候,isa指針就會指向一個中間類,而不是真實(shí)的類。所以 isa指針其實(shí)不需要指向?qū)嵗龑ο笳鎸?shí)的類。所以我們的程序最好不要依賴于isa指針。在調(diào)用類的方法的時候,最好要明確對象實(shí)例的類名
KVC(Key-Value-Coding)內(nèi)部的實(shí)現(xiàn):是鍵值編碼,一個對象在調(diào)用setValue的時候,(1)首先根據(jù)方法名找到運(yùn)行方法的時候所需要的環(huán) 境參數(shù)。(2)他會從自己isa指針結(jié)合環(huán)境參數(shù),找到具體的方法實(shí)現(xiàn)的接口。(3)再直接查找得來的具體的方法實(shí)現(xiàn)。Delegate:代理的目的是改變或傳遞控制鏈。允許一個類在某些特定時刻通知到其他類,而不需要獲取到那些類的指針??梢詼p少框架復(fù)雜度。消息的發(fā)送者(sender)告知接收者(receiver)某個事件將要發(fā)生,delegate同意然然后發(fā)送者響應(yīng)事件,delegate機(jī)制使得接收者可以改變發(fā)送者的行為。通常發(fā)送者和接收者的關(guān)系是直接的一對多的關(guān)系。
Notification:消息的發(fā)送者告知接收者事件已經(jīng)發(fā)生或者將要發(fā)送,僅此而已,接收者并不能反過來影響發(fā)送者的行為。通常發(fā)送者和接收者的關(guān)系是間接的多對多關(guān)系。
1).效率肯定是delegate比nsnotification高。
2).delegate方法比notification更加直接,最典型的特征是,delegate方法往往需要關(guān)注返回值,也就是delegate方法的結(jié)果。比如-windowShouldClose:,需要關(guān)心返回的是yes還是no。所以delegate方法往往包含should這個很傳神的詞。也就是好比你做我的delegate,我會問你我想關(guān)閉窗口你愿意嗎?你需要給我一個答案,我根據(jù)你的答案來決定如何做下一步。相反的,notification最大的特色就是不關(guān)心接受者的態(tài)度,我只管
把通告放出來,你接受不接受就是你的事情,同時我也不關(guān)心結(jié)果。所以notification往往用did這個詞匯,比如NSWindowDidResizeNotification,那么nswindow對象放出這個notification后就什么都不管了也不會等待接受者的反應(yīng)。
1)兩個模塊之間聯(lián)系不是很緊密,就用notification傳值,例如多線程之間傳值用notificaiton。
2)delegate只是一種較為簡單的回調(diào),且主要用在一個模塊中,例如底層功能完成了,需要把一些值傳到上層去,就事先把上層的函數(shù)通過delegate傳到底層,然后在底層call這個delegate,它們都在一個模塊中,完成一個功能,例如說 NavgationController 從 B 界面到A 點(diǎn)返回按鈕(調(diào)用popViewController方法)可以用delegate比較好。
What is lazy loading? 就是懶漢模式,只在用到的時候才去初始化。也可以理解成延時加載。我覺得最好也最簡單的一個列子就是tableView中圖片的加載顯示了。一個延時載,避免內(nèi)存過高,一個異步加載,避免線程堵塞。
9.OC有多繼承嗎?沒有的話可以用什么方法替代? 多繼承即一個子類可以有多個父類,它繼承了多個父類的特性。Object-c的類沒有多繼承,只支持單繼承,如果要實(shí)現(xiàn)多繼承的話,可以通過類別和協(xié)議的方式來實(shí)現(xiàn),OC類似于多繼承,是在用protocol委托代理來實(shí)現(xiàn)的;可以實(shí)現(xiàn)多個接口,通過實(shí)現(xiàn)多個接口可以完成C++的多重繼承;Category是類別,一般情況用分類好,用Category去重寫類的方法,僅對本Category有效,不會影響到其他類與原有類的關(guān)系。
10.分別描述類別(categories)和延展(extensions)是什么?以及兩者的區(qū)別?繼承和類別在實(shí)現(xiàn)中有何區(qū)別?為什么Category只能為對象添加方法,卻不能添加成員變量? 類別:在沒有原類.m文件的基礎(chǔ)上,給該類添加方法;
延展:一種特殊形式的類別,主要在一個類的.m文件里聲明和實(shí)現(xiàn)延展的作用,就是給某類添加私有方法或是私有變量。兩個的區(qū)別:延展可以添加屬性并且它添加的方法是必須要實(shí)現(xiàn)的。延展可以認(rèn)為是一個私有的類目。
繼承和類別在實(shí)現(xiàn)中的區(qū)別:類別可以在不獲悉,不改變原來代碼的情況下往里面添加新的方法,只能添加,不能刪除修改。并且如果類別和原來類中的方法產(chǎn)生名稱沖突,則類別將覆蓋原來的方法,因?yàn)轭悇e具有更高的優(yōu)先級。Category只能為對象添加方法,卻不能添加成員變量的原因:如果可以添加成員變量,添加的成員變量沒有辦法初始化
11.Objective-C有私有方法么?私有變量呢?如多沒有的話,有沒有什么代替的方法? objective-c類里面的方法只有兩種, 靜態(tài)方法和實(shí)例方法.但是可以通過把方法的聲明和定義都放在.m文件中來實(shí)現(xiàn)一個表面上的私有方法。有私有變量,可以通過@private來修飾,或者把聲明放到.m文件中。在Objective‐C中,所有實(shí)例變量默認(rèn)都是私有的,所有實(shí)例方法默認(rèn)都是公有的
12.#include與#import的區(qū)別? #import與@class的區(qū)別? #import指令是Object-C針對#include的改進(jìn)版本,#import確保引用的文件只會被引用一次,這樣你就不會陷入遞歸包含的問題中。
#import與@class二者的區(qū)別在于:
1>#import會鏈入該頭文件的全部信息,包括實(shí)體變量和方法等;而@class只是告訴編譯器,其后面聲明的名稱是類的名稱,至于這些類是如何定義的,暫時不用考慮。
2>在頭文件中,一般只需要知道被引用的類的名稱就可以了。不需要知道其內(nèi)部的實(shí)體變量和方法,所以在頭文件中一般使用@class來聲明這個名稱是類的名稱。而在實(shí)現(xiàn)類里面,因?yàn)闀玫竭@個引用類的內(nèi)部的實(shí)體變量和方法,所以需要使用#import來包含這個被引用類的頭文件。
3>在編譯效率方面考慮,如果你有100個頭文件都#import了同一個頭文件,或者這些文件是依次引用的,如A–>B, B–>C, C–>D這樣的引用關(guān)系。當(dāng)最開始的那個頭文件有變化的話,后面所有引用它的類都需要重新編譯,如果你的類有很多的話,這將耗費(fèi)大量的時間。而是用@class則不會。
4>如果有循環(huán)依賴關(guān)系,如:A–>B, B–>A這樣的相互依賴關(guān)系,如果使用#import來相互包含,那么就會出現(xiàn)編譯錯誤,如果使用@class在兩個類的頭文件中相互聲明,則不會有編譯錯誤出現(xiàn)。所以,一般來說,@class是放在interface中的,只是為了在interface中引用這個類,把這個類作為一個類型來用的。在實(shí)現(xiàn)這個接口的實(shí)現(xiàn)類中,如果需要引用這個類的實(shí)體變量或者方法之類的,還是需要import在@class中聲明的類進(jìn)來.13.淺復(fù)制和深復(fù)制的區(qū)別?(Difference between shallow copy and deep copy?)淺層復(fù)制(copy):只復(fù)制指向?qū)ο蟮闹羔?,而不?fù)制引用對象本身。意思就是說我有個A對象,復(fù)制一份后得到A_copy對象后,對于淺復(fù)制來說,A和A_copy指向的是同一個內(nèi)存資源,復(fù)制的只不過是是一個指針,對象本身資源還是只有一份,那如果我們對A_copy執(zhí)行了修改操作,那么發(fā)現(xiàn)A引用的對象同樣被修改,這其實(shí)違背了我們復(fù)制拷貝的一個思想。
深層復(fù)制(mutableCopy):復(fù)制引用對象本身。深復(fù)制就好理解了,內(nèi)存中存在了兩份獨(dú)立對象本身,當(dāng)修改A時,A_copy不變。
用網(wǎng)上一哥們通俗的話將就是:
淺復(fù)制好比你和你的影子,你完蛋,你的影子也完蛋
深復(fù)制好比你和你的克隆人,你完蛋,你的克隆人還活著。
14.類變量的@protected,@private,@public,@package聲明各有什么含義? 變量的作用域不同,@protected 該類和所有子類中的方法可以直接訪問這樣的變量,這是默認(rèn)的;@private 該類中的方法可以訪問這樣的變量,子類不可以;@public除了自己和子類方法外,也可以被其他類或者其他模塊中的方法訪問;@package 目前尚未得出結(jié)論.15.static 關(guān)鍵字的作用? 1>函數(shù)體內(nèi) static 變量的作用范圍為該函數(shù)體,不同于 auto 變量,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時仍維持上次的值;
2>在模塊內(nèi)的 static 全局變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問;
3>在模塊內(nèi)的 static 函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個函數(shù)的使用范圍被限制在聲明 它的模塊內(nèi);
4>在類中的 static 成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
5>在類中的 static 成員函數(shù)屬于整個類所擁有,這個函數(shù)不接收 this 指針,因而只能訪問類的static 成員變量
16.關(guān)鍵字volatile有什么含意?并給出三個不同的例子
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設(shè)這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
? 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
? 一個中斷服務(wù)子程序中會訪問到的非自動變量(Non-automatic variables)? 多線程應(yīng)用中被幾個任務(wù)共享的變量
17.Objective-C與C、C+++之間的聯(lián)系和區(qū)別?
Objective-C和C++都是C的面向?qū)ο蟮某?/p>
Object與C++的區(qū)別主要點(diǎn):Objective-C是完全動態(tài)的,支持在運(yùn)行時動態(tài)類型決議(dynamic typing),動態(tài)綁定(dynamic binding)以及動態(tài)裝載(dynamic loading);而C++是部分動態(tài)的,編譯時靜態(tài)綁定,通過嵌入類(多重繼承)和虛函數(shù)(虛表)來模擬實(shí)現(xiàn)。
Objective-C 在語言層次上支持動態(tài)消息轉(zhuǎn)發(fā),其消息發(fā)送語法為 [object function];而且C++ 為 object->function()。兩者的語義也不同,在 Objective-C 里是說發(fā)送消息到一個對象上,至于這個對象能不能響應(yīng)消息以及是響應(yīng)還是轉(zhuǎn)發(fā)消息都不會 crash;而在 C++ 里是說對象進(jìn)行了某個操作,如果對象沒有這個操作的話,要么編譯會報(bào)錯(靜態(tài)綁定),要么程序會 crash 掉的(動態(tài)綁定)。
18.目標(biāo)-動作機(jī)制
目標(biāo)是動作消息的接收者。一個控件,或者更為常見的是它的單元,以插座變量(參見“插座變量”部分)的形式保有其動作消息的目標(biāo)。
動作是控件發(fā)送給目標(biāo)的消息,或者從目標(biāo)的角度看,它是目標(biāo)為了響應(yīng)動作而實(shí)現(xiàn)的方法.程序需要某些機(jī)制來進(jìn)行事件和指令的翻譯。這個機(jī)制就是目標(biāo)-動作機(jī)制。
19.動態(tài)綁定
在運(yùn)行時確定要調(diào)用的方法 , 動態(tài)綁定將調(diào)用方法的確定也推遲到運(yùn)行時。在編譯時,方法的調(diào)用并不和代碼綁定在一起,只有在消實(shí)發(fā)送出來之后,才確定被調(diào)用的代碼。通過
動態(tài)類型和動態(tài)綁 定技術(shù),代碼每次執(zhí)行都可以得到不同的結(jié)果。運(yùn)行時因子負(fù)責(zé)確定消息的接收者和被調(diào)用的方法。運(yùn)行時的消息分發(fā)機(jī)制為動態(tài)綁定提供支持。當(dāng)向一個動態(tài)類型確定了的對象發(fā)送消息時,運(yùn)行環(huán)境系統(tǒng)會通過接收者的isa指針定位對象的類,并以此為起點(diǎn)確定被調(diào)用的方法,方法和消息是動態(tài)綁定的。而且,不必在Objective-C 代碼中做任何工作,就可以自動獲取動態(tài)綁定的好處。在每次發(fā)送消息時,特別是當(dāng)消息的接收者是動態(tài)類型已經(jīng)確定的對象時,動態(tài)綁定就會例行而透明地發(fā)生
20.iPad開發(fā)與iPhone開發(fā)有什么不同?
開發(fā)iPad和iPhone應(yīng)用使用的是相同的SDK,它們之間的區(qū)別主要體現(xiàn)在設(shè)備硬件和UI操作上。
(1)設(shè)備硬件:由于iPad不具備電話功能,iPad1也沒有相機(jī),所以相關(guān)的特性如電話、SMS等無法使用。
(2)UI操作:由于iPad屏幕大小為9.7英寸,而iPhone的屏幕是3.5英寸,這就決定了兩者操作方式的不同。有些控件,如UIPopoverController, UISliptViewController 只能在iPad中使用,而不能在iPhone中使用;而且當(dāng)需要presentModalViewController時,Window presentation style 只對iPad有效;除此之外,iPad產(chǎn)品開發(fā)中還需針對iPad大屏幕考慮加入多指手勢以增加用戶體驗(yàn)
21.換膚技術(shù)使用了什么技術(shù)? 22.談?wù)勀銓untime的了解? 23.算法和數(shù)據(jù)結(jié)構(gòu)
第四篇:黑馬程序員:IOS面試寶典之c語言
黑馬程序員:IOS面試寶典之c語言
C語言語法
1.局部變量能否和全局變量重名?
答:能,局部會屏蔽全局。要用全局變量,需要使用“::”;局部變量可以與全局變量同名,在函數(shù)內(nèi)引用這個變量時,會用到同名的局部變量,而不會用到全局變量。對于有些編譯器而言,在同一個函數(shù)內(nèi)可以定義多個同 名的局部變量,比如在兩個循環(huán)體內(nèi)都定義一個同名的局部變量,而那個局部變量的作用域就在那個循環(huán)體內(nèi)。
2.如何引用一個已經(jīng)定義過的全局變量?
答:extern 可以用引用頭文件的方式,也可以用extern關(guān)鍵字,如果用引用頭文件方式來引用某個在頭文件中聲明的全局變理,假定你將那個編寫錯了,那么在編譯期 間會報(bào)錯,如果你用extern方式引用時,假定你犯了同樣的錯誤,那么在編譯期間不會報(bào)錯,而在連接期間報(bào)錯。
3.全局變量可不可以定義在可被多個.C文件包含的頭文件中?為什么?
答:可以,在不同的C文件中以static形式來聲明同名全局變量。
可以在不同的C文件中聲明同名的全局變量,前提是其中只能有一個C文件中對此變量賦初值,此時連接不會出錯.4.關(guān)鍵字volatile有什么含意?并舉出三個不同的例子?
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設(shè)這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1)并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
2)一個中斷服務(wù)子程序中會訪問到的非自動變量(Non-automatic variables)3)多線程應(yīng)用中被幾個任務(wù)共享的變量
5.static 關(guān)鍵字的作用是什么?
(1)函數(shù)體內(nèi) static 變量的作用范圍為該函數(shù)體,不同于 auto 變量,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時仍維持上次的值;
(2)在模塊內(nèi)的 static 全局變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問;
(3)在模塊內(nèi)的 static 函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個函數(shù)的使用范圍被限制在聲明
它的模塊內(nèi);
(4)在類中的 static 成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
(5)在類中的 static 成員函數(shù)屬于整個類所擁有,這個函數(shù)不接收 this 指針,因而只能訪問類的static 成員變量。
6.堆和棧的區(qū)別是什么?
管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。
申請大?。簵#涸赪indows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng) 預(yù)先規(guī)定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數(shù)),如果申請的空間超過棧的剩余空間時,將提示 overflow。因此,能從棧獲得的空間較小。堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地 址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。
碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對于棧來講,則不會存在這個 問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對應(yīng),以至于永遠(yuǎn)都不可能有一個內(nèi)存塊從棧中間彈出 分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動態(tài)分配由 alloca函數(shù)進(jìn)行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。
分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的 效率比較高。堆則是C/C++函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的
7.static全局變量與普通的全局變量有什么區(qū)別?static局部變量和普通局部變量有什么區(qū)別?static函數(shù)與普通函數(shù)有什么區(qū)別?
答: 1)全局變量(外部變量)的說明之前再冠以static 就構(gòu)成了靜態(tài)的全局變量。全局變量本身就是靜態(tài)存儲方式,靜態(tài)全局變量當(dāng)然也是靜態(tài)存儲方式。這兩者在存儲方式上并無不同。這兩者的區(qū)別在于非靜態(tài)全局變量的作用域是整個源程序,當(dāng)一個源程序由多個源文件組成時,非靜態(tài)的全局變量在各個源文件中都是有效的。而靜態(tài)全局變量則限制了其作用域,即只在定義該變量的源文件內(nèi)有效,在同一源程序的其它源文件中不能使用它。由于靜態(tài)全局變量的作用域局限于一個源文件內(nèi),只能為該源文件內(nèi)的函數(shù)公用,因此可以避免在其它源文件中引起錯誤。
2)從以上分析可以看出,把局部變量改變?yōu)殪o態(tài)變量后是改變了它的存儲方式即改變了它的生存期。把全局變量改變?yōu)殪o態(tài)變量后是改變了它的作用域,限制了它的使用范圍。
3)static函數(shù)與普通函數(shù)作用域不同,僅在本文件。只在當(dāng)前源文件中使用的函數(shù)應(yīng)該說明為內(nèi)部函數(shù)(static),內(nèi)部函數(shù)應(yīng)該在當(dāng)前源文件中說明和定義。對于可在當(dāng)前源文件以外使用的函數(shù),應(yīng)該在一個頭文件中說明,要使用這些函數(shù)的源文件要包含這個頭文件
綜上所述: static全局變量與普通的全局變量有什么區(qū)別:
static全局變量只初使化一次,防止在其他文件單元中被引用;
static局部變量和普通局部變量有什么區(qū)別:
static局部變量只被初始化一次,下一次依據(jù)上一次結(jié)果值;
static函數(shù)與普通函數(shù)有什么區(qū)別:
static函數(shù)在內(nèi)存中只有一份,普通函數(shù)在每個被調(diào)用中維持一份拷貝
8.關(guān)鍵字const的作用分別是什么?
const int a;int const a;const int *a;int const *a;int * const a;int const * const a;1> 前兩個的作用是一樣:a 是一個常整型數(shù)
2> 第三、四個意味著 a 是一個指向常整型數(shù)的指針(整型數(shù)是不可修改的,但指針可以)3> 第五個的意思:a 是一個指向整型數(shù)的常指針(指針指向的整型數(shù)是可以修改的,但指針是不可修改的)4> 最后一個意味著:a 是一個指向常整型數(shù)的常指針(指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)9.struct與union的區(qū)別是什么?
設(shè)有以下說明和定義:
typedef union
{ long i;int k[5];char c;} DATE;
struct data { int cat;DATE cow;double dog;} too;
DATE max;
則語句 printf(“%d”,sizeof(struct data)+sizeof(max));的執(zhí)行結(jié)果是:___52____ 考點(diǎn):區(qū)別struct與union.(一般假定在32位機(jī)器上)
答:DATE是一個union, 變量公用空間.里面最大的變量類型是int[5], 占用20個字節(jié).所以它的大小是20.data是一個struct, 每個變量分開占用空間.依次為int4 + DATE20 + double8 = 32.所以結(jié)果是 20 + 32 = 52.當(dāng)然...在某些16位編輯器下, int可能是2字節(jié),那么結(jié)果是 int2 + DATE10 + double8 = 20 10.關(guān)于指針的理解
1.請寫出下題輸出結(jié)果:
1.Main(){ 2.Int a[5] = {1,2,3,4,5};
3.Int *ptr =(int *)(&a+1);4.Printf(“%d,%d”,*(a+1),*(ptr – 1));5.} 答:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],執(zhí)行結(jié)果是2.5,&a+1不是首地址+1,系統(tǒng)會認(rèn)為加一個a數(shù)組的偏 移,是偏移了一個數(shù)組的大?。ū纠?個int,int *ptr=(int *)(&a+1);則ptr實(shí)際 是&(a[5]),也就是a+5原因如下:
&a是數(shù)組指針,其類型為 int(*)[5];而指針加1要根據(jù)指針類型加上一定的值,不同類型的指針+1之后增加的大小不同。a是長度為5的int數(shù)組指針,所以要加 5*sizeof(int)所以ptr實(shí)際是a[5],但是prt與(&a+1)類型是不一樣的(這點(diǎn)很重要),所以prt-1只會減去sizeof(int*),a,&a的地址是一樣的,但意思不一樣,a是數(shù)組首地址,也就是a[0]的地址,&a是對象(數(shù)組)首地址,a+1是數(shù)組下一元素的地址,即a[1],&a+1是下一個對象的地址,即a[5].2.寫出下列代碼的輸出內(nèi)容
#include
int inc(int a)
{ return(++a);}
int multi(int*a,int*b,int*c)
{
return(*c=*a**b);}
typedef int(FUNC1)(int in);
typedef int(FUNC2)(int*,int*,int*);
void show(FUNC2 fun,int arg1, int*arg2)
{
FUNC1 p=&inc;
int temp =p(arg1);
fun(&temp,&arg1, arg2);
printf(“%dn”,*arg2);
}
main()
{
int a;
//局部變量a為0;
show(multi,10,&a);
return 0;
}
答:110
3.以下為Windows NT下的32位C++程序,請計(jì)算sizeof的值void Func(char str[100]){
sizeof(str)= ? } void *p = malloc(100);sizeof(p)= ? 答案:這題 很常見了,Func(char str[100])函數(shù)中數(shù)組名作為函數(shù)形參時,在函數(shù)體內(nèi),數(shù)組名失去了本身的內(nèi)涵,僅僅只是一個指針;在失去其內(nèi)涵的同時,它還失去了其常量特性,可以作自增、自減等 操作,可以被修改。Windows NT 32位平臺下,指針的長度(占用內(nèi)存的大?。?字節(jié),故sizeof(str)、sizeof(p)都為4。
第五篇:黑馬程序員:IOS面試寶典之UITableView與UICollectionView
黑馬程序員:IOS面試寶典之UITableView與UICollectionView 1.UITableView的重用機(jī)制?(或者如何在一個view上顯示多個tableView,tableView要求不同的數(shù)據(jù)源以及不同的樣式(要求自定義cell), 如何組織各個tableView的delegate和dataSource?請說說實(shí)現(xiàn)思路?)查看UITableView頭文件,會找到NSMutableArray*visiableCells,和NSMutableArray*reusableTableCells兩 個結(jié)構(gòu)。visiableCells內(nèi)保存當(dāng)前顯示的cells,reusableTableCells保存可重用的cells。TableView顯示之初,reusableTableCells為空,那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。開始的cell都是通過 [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] 來創(chuàng) 建,而且cellForRowAtIndexPath只是調(diào)用最大顯示cell數(shù)的次數(shù)。比如:有100條數(shù)據(jù),iPhone一屏最多顯示10個cell。程序最開始顯示TableView的情況是: 1.用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] 創(chuàng)建10次cell,并給cell指定同樣的重用標(biāo)識(當(dāng)然,可以為不同顯示類型的cell指定不同的標(biāo)識)。并且10個cell全部都加 入到 visiableCells數(shù)組,reusableTableCells為空。
2.向下拖動tableView,當(dāng)cell1完全移出屏幕,并且 cell11(它也是alloc出來的,原因同上)完全顯示出來的時候。cell11加入到visiableCells,cell1移出 visiableCells,cell1加入到reusableTableCells。
3.接著向下拖動tableView,因?yàn)閞eusableTableCells中已 經(jīng)有值,所以,當(dāng)需要顯示新的cell, cellForRowAtIndexPath再次被調(diào)用的時 候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1 移出reusableTableCells;cell2移出 visiableCells,cell2加入到reusableTableCells。之后再需要顯示的Cell就可 以正常重用了
2.在一個tableView 中需要自定義多種樣式的cell(兩種或三種),通常你如何實(shí)現(xiàn),說說思路即可? 比如:有100條數(shù)據(jù),iPhone一屏最多顯示10個cell。程序最開始顯示TableView的情況是: 1.用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] 創(chuàng)建10次cell,并給cell指定同樣的重用標(biāo)識(當(dāng)然,可以為不同顯示類型的cell指定不同的標(biāo)識)。并且10個cell全部都加 入到 visiableCells數(shù)組,reusableTableCells為空。
2.向下拖動tableView,當(dāng)cell1完全移出屏幕,并且 cell11(它也是alloc出來的,原因同上)完全顯示出來的時候。cell11加入到visiableCells,cell1移出 visiableCells,cell1加入到reusableTableCells。
3.接著向下拖動tableView,因?yàn)閞eusableTableCells中已 經(jīng)有值,所以,當(dāng)需要顯示新的cell, cellForRowAtIndexPath再次被調(diào)用的時 候,tableView dequeueReusableCellWithIdentifier:CellIdentifier,返回cell1。cell1加入到visiableCells,cell1 移出reusableTableCells;cell2移出 visiableCells,cell2加入到
reusableTableCells。之后再需要顯示的Cell就可 以正常重用了
3.UITableView的性能優(yōu)化? 滑動的時候有種卡的感覺是為什么?怎么解決?
然而在使用第三方應(yīng)用時,卻經(jīng)常遇到性能上的問題,普遍表現(xiàn)在滾動時比較卡,特別是table cell中包含圖片的情況時。實(shí)際上針對性地優(yōu)化一下就可以解決tableView滑動的時候卡頓的問題, 在iOS應(yīng)用中,UITableView應(yīng)該是使用率最高的視圖之一了。iPod、時鐘、日歷、備忘錄、Mail、天氣、照片、電話、短信、Safari、App Store、iTunes、Game Center?幾乎所有自帶的應(yīng)用中都能看到它的身影,可見它的重要性。然而在使用第三方應(yīng)用時,卻經(jīng)常遇到性能上的問題,普遍表現(xiàn)在滾動時比較卡,特別是table cell中包含圖片的情況時。
實(shí)際上只要針對性地優(yōu)化一下: 1>同一時間其實(shí)只需要存在一屏幕的cell對象即可,不需要為每一行創(chuàng)建一個cell。
UITableView是UIScrollView的子類,因此它可以自動響應(yīng)滾動事件(一般為上下滾動)。它內(nèi)部包含0到多個UITableViewCell對象,每個table cell展示各自的內(nèi)容。當(dāng)新cell需要被顯示時,就會調(diào)用tableView:cellForRowAtIndexPath:方法來獲取或創(chuàng)建一個 cell;而不可視時,它又會被釋放。由此可見,同一時間其實(shí)只需要存在一屏幕的cell對象即可,不需要為每一行創(chuàng)建一個cell。此 外,UITableView還可以分為多個sections,每個區(qū)段都可以有自己的head、foot和cells。而在定位一個cell時,就需要2 個字段了:在哪個section,以及在這個section的第幾行。這在iOS SDK中是用NSIndexPath來表述的,UIKit為其添加了indexPathForRow:inSection:這個創(chuàng)建方法。其他諸如編輯之類的就不提了,因?yàn)楹捅疚臒o關(guān)。
介紹完原理,接下來就開始優(yōu)化吧。
使用不透明視圖。
不透明的視圖可以極大地提高渲染的速度。因此如非必要,可以將table cell及其子視圖的opaque屬性設(shè)為YES(默認(rèn)值)。其中的特例包括背景色,它的alpha值應(yīng)該為1(例如不要使用clearColor);圖像的alpha值也應(yīng)該為1,或者在畫圖時設(shè)為不透明。
不要重復(fù)創(chuàng)建不必要的table cell。
前面說了,UITableView只需要一屏幕的UITableViewCell對象即可。因此在cell不可見時,可以將其緩存起來,而在需要時繼續(xù)使用它即可。而UITableView也提供了這種機(jī)制,只需要簡單地設(shè)置一個identifier即可:
static NSString *CellIdentifier = @“xxx”;UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];if(cell == nil){ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];} 值得一提的是,cell被重用時,它內(nèi)部繪制的內(nèi)容并不會被自動清除,因此你可能需要調(diào)用setNeedsDisplayInRect:或setNeedsDisplay方法。此 外,在添加table cell的時候,如果不需要動畫效果,最好不要使用insertRowsAtIndexPaths:withRowAnimation:方法,而是直接調(diào) 用reloadData方法。因?yàn)榍罢邥λ衖ndexPaths調(diào)用tableView:cellForRowAtIndexPath:方法,即便該 cell并不需要顯示(不知道是不是bug),這就可能創(chuàng)建大量多余的cell。勘誤:只是在模擬器上測試如此,真機(jī)調(diào)試時沒有這種bug。
減少視圖的數(shù)目。
UITableViewCell包含了textLabel、detailTextLabel和imageView等view,而
你還可以自定義一些視圖放在它的contentView里。然而view是很大的對象,創(chuàng)建它會消耗較多資源,并且也影響渲染的性能。如果你的table cell包含圖片,且數(shù)目較多,使用默認(rèn)的UITableViewCell會非常影響性能。奇怪的是,使用自定義的view,而非預(yù)定義的view,明顯會快些。當(dāng)然,最佳的解決辦法還是繼承UITableViewCell,并在其drawRect:中自行繪制:
-(void)drawRect:(CGRect)rect { if(image){ [image drawAtPoint:imagePoint];self.image = nil;} else { [placeHolder drawAtPoint:imagePoint];} [text drawInRect:textRect withFont:font lineBreakMode:UILineBreakModeTailTruncation];} 不過這樣一來,你會發(fā)現(xiàn)選中一行后,這個cell就變藍(lán)了,其中的內(nèi)容就被擋住了。最簡單的方法就是將cell的selectionStyle屬性設(shè)為UITableViewCellSelectionStyleNone,這樣就不會被高亮了。此外還可以創(chuàng)建CALayer,將內(nèi)容繪制到layer上,然后對cell的contentView.layer調(diào)用addSublayer:方法。這個例 子中,layer并不會顯著影響性能,但如果layer透明,或者有圓角、變形等效果,就會影響到繪制速度了。解決辦法可參見后面的預(yù)渲染圖像。
不要做多余的繪制工作。
在實(shí)現(xiàn)drawRect:的時候,它的rect參數(shù)就是需要繪制的區(qū)域,這個區(qū)域之外的不需要進(jìn)行繪制。例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text,然后再調(diào)用繪制方法。
預(yù)渲染圖像。
你會發(fā)現(xiàn)即使做到了上述幾點(diǎn),當(dāng)新的圖像出現(xiàn)時,仍然會有短暫的停頓現(xiàn)象。解決的辦法就是在bitmap context里先將其畫一遍,導(dǎo)出成UIImage對象,然后再繪制到
屏幕,詳細(xì)做法可見《利用預(yù)渲染加速iOS設(shè)備的圖像顯示》。
不要阻塞主線程。
做到前幾點(diǎn)后,你的table view滾動時應(yīng)該足夠流暢了,不過你仍可能讓用戶感到不爽。常見的現(xiàn)象就是在更新數(shù)據(jù)時,整個界面卡住不動,完全不響應(yīng)用戶請求。出現(xiàn)這種現(xiàn)象的原因就是主線程執(zhí)行了耗時很長的函數(shù)或方法,在其執(zhí)行完畢前,無法繪制屏幕和響應(yīng)用戶請求。其中最常見的就是網(wǎng)絡(luò)請求了,它通常都需要花費(fèi)數(shù)秒的時間,而你不應(yīng)該讓用戶等待那么久。解決辦法就是使用多線程,讓子線程去執(zhí)行這些函數(shù)或方法。這里面還有一個學(xué)問,當(dāng)下載線程數(shù)超過2時,會顯著影響主線程的性能。因此在使用 ASIHTTPRequest時,可以用一個NSOperationQueue
來維護(hù)下載請求,并將其
maxConcurrentOperationCount設(shè)為2。而NSURLRequest則可以配合GCD來實(shí)現(xiàn),或者使用NSURLConnection的setDelegateQueue:方法。當(dāng)然,在不需要響應(yīng)用戶請求時,也可以增加下載線程數(shù),以加快下載速度:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if(!decelerate){ queue.maxConcurrentOperationCount = 5;} }(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { queue.maxConcurrentOperationCount = 2;} 此外,自動載入更新數(shù)據(jù)對用戶來說也很友好,這減少了用戶等待下載的時間。例如每次載入50條信息,那就可以在滾動到倒數(shù)第10條以內(nèi)時,加載更多信息:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if(count-indexPath.row < 10 &&!updating){ updating = YES;[self update];} }// update方法獲取到結(jié)果后,設(shè)置updating為NO 還有一點(diǎn)要注意的就是當(dāng)圖片下載完
成后,如果cell是可見的,還需要更新圖像:
NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];for(NSIndexPath *visibleIndexPath in indexPaths){ if(indexPath == visibleIndexPath){ MyTableViewCell *cell =(MyTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];cell.image = image;[cell setNeedsDisplayInRect:imageRect];break;} }// 也可不遍歷,直接與頭尾相比較,看是否在中間即可。最后還是前面所說過的insertRowsAtIndexPaths:withRowAnimation:方法,插入新行需要在主線程執(zhí)行,而一次插入很多行的話(例如50行),會長時間阻塞主線程。而換成reloadData方法的話,瞬間就處理完了。l 4 tableview的cell里如何嵌套collection view?
思路同網(wǎng)易新聞類似, 用自定義的繼承自UITableViewCell的類, 在initWithFrame的構(gòu)造方法中, 初始化自定義的繼承自UICollectionView的類 下拉和上拉的原理? 上拉和下拉的原理可以參照新浪微博的上拉和下拉刷新, 以tableView的上拉刷新為例:
1> 為了進(jìn)行無縫閱讀, 通過tableView的代理方法, willDisplayCell判斷是否是最后一行,2> 如果是最后一行, 在顯示最后一行的同時, 判斷當(dāng)前是否存在上拉刷新 3> 如果當(dāng)前沒有上拉刷新, 就進(jìn)行加載數(shù)據(jù), 啟動”橘花”
以tableView的下拉刷新為例: 1> 判斷當(dāng)前的上拉刷新視圖是否動畫
2> 如果沒有動畫, 就不是上拉刷新 3> 然后下拉刷新加載數(shù)據(jù) 4> 加載完畢數(shù)據(jù)關(guān)閉刷新