關(guān)鍵詞/關(guān)鍵類
1.#import跟#include有什么區(qū)別,@class呢,#import<>跟#import<>有什么區(qū)別?
#include c語言中引入一個頭文件,但是可能出現(xiàn)交叉編譯
#import在OC中引入自己創(chuàng)建的頭文件#import””或者系統(tǒng)框架#import<>。
#import不會出現(xiàn)交叉編譯,@class對一個類進行聲明,告訴編譯器有這個類,但是類的定義什么的都不知道
2描述一下KVO和KVC?
Key-Value Observing (簡寫為KVO):當(dāng)指定的對象的屬性被修改了,允許對象接受到通知的機制。每次指定的被觀察對象的屬性被修改的時候,KVO都會自動的去通知相應(yīng)的觀察者。
KVC是KeyValue Coding的簡稱,它是一種可以直接通過字符串的名字(key)來訪問類屬性的機制。而不是通過調(diào)用Setter、Getter方法訪問。
3.類目和繼承地區(qū)別
1.類別是對方法的擴展,不能添加成員變量。繼承可以在原來父類的成員變量的基礎(chǔ)上,添加新的成員變量
2.類別只能添加新的方法,不能修改和刪除原來的方法。繼承可以增加、修改和刪除方法。
3.類別不提倡對原有的方法進行重載。繼承可以通過使用super對原來方法進行重載。
4.類別可以被繼承,如果一個父類中定義了類別,那么其子類中也會繼承此類別。
5.什么是懶加載?在使用懶加載時的注意事項是什么?
答:所謂的懶加載指的是延遲創(chuàng)建對象,只有當(dāng)需要的時候才創(chuàng)建對象。在真正開發(fā)的過程中其實懶加載就是重寫getter方法。在getter方法的內(nèi)部,實現(xiàn)對象的創(chuàng)建,如果對象為nil才創(chuàng)建,如果不為nil,直接返回對象。在真正使用懶加載時需要注意的是當(dāng)?shù)谝淮问褂脤ο髸r,需要調(diào)用self.因為只有這樣才能調(diào)用對應(yīng)的getter方法,對象才會被創(chuàng)建。
6.簡述你對UIView、UIWindow、CALayer的理解。
答:CALayer是圖層類,本身可以顯示的,但是不能響應(yīng)事件。
UIView是iOS系統(tǒng)中界面元素的基礎(chǔ),所有的界面元素都繼承自它。事件的處理由它來執(zhí)行,但是顯示其實是由其對應(yīng)的layer層來操作的,UIView內(nèi)嵌了一個layer,layer顯示內(nèi)容,UIView本身增加了事件處理的功能。
UIWindow繼承自UIView,主要的作用是作為窗口呈現(xiàn)其他的視圖。而且一個應(yīng)用程序一般情況下只有一個窗口。
7. id聲明的對象有什么特性?
答:id是任意對象類型的,不能表示基本類型。id類型是通用指針類型,因為通過指針,也就是內(nèi)存地址來引用對象,所以可以將任意對象賦值給id類型的對象。返回id類型值的方法是返回指向內(nèi)存中某對象的指針。然后可以將該值賦給任何對象變量(強制類型轉(zhuǎn)換即可)。因為無論在哪里,對象總是攜帶它的isa成員。所以即使將它存儲在id類型的通用對象變量中,也總是可以確定它的真實類型,id是多態(tài)的一種體現(xiàn)
7.@synthesize和@dynamic有什么區(qū)別?
(1)@property有兩個對應(yīng)的詞,一個是@synthesize,一個是@dynamic。如果@synthesize和@dynamic都沒寫,那么默認的就是@syntheszie var = _var;
(2)@synthesize的語義是如果你沒有手動實現(xiàn)setter方法和getter方法,那么編譯器會自動為你加上這兩個方法。
(3)@dynamic告訴編譯器:屬性的setter與getter方法由用戶自己實現(xiàn),不自動生成。(當(dāng)然對于readonly的屬性只需提供getter即可)。假如一個屬性被聲明為@dynamic var,然后你沒有提供@setter方法和@getter方法,編譯的時候沒問題,但是當(dāng)程序運行到instance.var = someVar,由于缺setter方法會導(dǎo)致程序崩潰;或者當(dāng)運行到someVar = instance.var時,由于缺getter方法同樣會導(dǎo)致崩潰。編譯時沒問題,運行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定。@dynamic可用于在分類中添加屬性(需要用到objc_getAssociatedObject和objc_setAssociatedObject函數(shù))。
3.在Category中本身不允許為已有的類添加新的屬性或者成員變量,你有沒有其他的方法可以在category中添加屬性或者是成員變量?
答:一種方法就是使用runtime.h中的objc_getAssociatedObject和objc_setAssociatedObject來訪問和生成關(guān)聯(lián)對象。例如為NSObject添加一個類目,分類中添加一個屬性。代碼如下所示:
NSObject+Test.h文件
#import
@interface NSObject (Test)
@property (nonatomic, strong) NSString *test;
@end
NSObject+Test.m文件
#import “NSObject+Test.h"
#import
static const void *instanceNameKey = &instanceNameKey;
@implementation NSObject (Test)
@dynamic test;
- (NSString *)test
{
return objc_getAssociatedObject(self, instanceNameKey);
}
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, instanceNameKey, test, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
15.在oc中引入頭文件使用的關(guān)鍵字是哪一個?能在c語言文件中使用嗎?
答:#import關(guān)鍵字可以在OC中使用,不能在C文件中使用,#include可以在C和OC中使用。
16.#import與#include相比,好處是什么?
答:#import確定一個文件只能被導(dǎo)入一次,避免的重復(fù)導(dǎo)入的問題,使用#include一定要注意重復(fù)導(dǎo)入的問題。所以在OC中都使用#import來引用頭文件。
17.#import<>和#import””的區(qū)別是什么?
答:#import<>用于對系統(tǒng)文件的引用,編譯器會在系統(tǒng)文件目錄中去查找文件
#import””用于對自定義的文件的引用,編譯器首先回去用戶目錄下查找,然后去安裝目錄,最后去系統(tǒng)目錄中查找文件。
18.@class的作用是什么?
答:@class的作用是告訴編譯器有@class后面的內(nèi)容是一個類名。只是告訴編譯器存在這么一個類,類具體包含哪些方法,屬性和變量的并沒有告訴編譯器。一般在類的頭文件中使用@class來引入其他類。
55.簡述你對UIView、UIWindow、CALayer的理解。
答:CALayer是圖層類,本身可以顯示的,但是不能響應(yīng)事件。
UIView是iOS系統(tǒng)中界面元素的基礎(chǔ),所有的界面元素都繼承自它。事件的處理由它來執(zhí)行,但是顯示其實是由其對應(yīng)的layer層來操作的,UIView內(nèi)嵌了一個layer,layer顯示內(nèi)容,UIView本身增加了事件處理的功能。
UIWindow繼承自UIView,主要的作用是作為窗口呈現(xiàn)其他的視圖。而且一個應(yīng)用程序一般情況下只有一個窗口。
2.Sizeof和strlen的區(qū)別和聯(lián)系?
sizeof(...)是運算符,在頭文件中typedef為unsigned int,其值在編譯時即計算好了,參數(shù)可以是數(shù)組、指針、類型、對象、函數(shù)等。
它的功能是:獲得保證能容納實現(xiàn)所建立的最大對象的字節(jié)大小。
由于在編譯時計算,因此sizeof不能用來返回動態(tài)分配的內(nèi)存空間的大小。實際上,用sizeof來返回類型以及靜態(tài)分配的對象、結(jié)構(gòu)或數(shù)組所占的空間,返回值跟對象、結(jié)構(gòu)、數(shù)組所存儲的內(nèi)容沒有關(guān)系。
strlen(...)是函數(shù),要在運行時才能計算。參數(shù)必須是字符型指針(char*)。當(dāng)數(shù)組名作為參數(shù)傳入時,實際上數(shù)組就退化成指針了。
它的功能是:返回字符串的長度。該字符串可能是自己定義的,也可能是內(nèi)存中隨機的,該函數(shù)實際完成的功能是從代表該字符串的第一個地址開始遍歷,直到遇到結(jié)束符NULL。返回的長度大小不包括NULL。
區(qū)別和聯(lián)系:
1.sizeof操作符的結(jié)果類型是size_t,它在頭文件中typedef為unsigned int類型。
該類型保證能容納實現(xiàn)所建立的最大對象的字節(jié)大小。
2.sizeof是運算符,strlen是函數(shù)。
3.sizeof可以用類型做參數(shù),strlen只能用char*做參數(shù),且必須是以''\0''結(jié)尾的。
sizeof還可以用函數(shù)做參數(shù),比如:
short f();
printf("%d\n", sizeof(f()));
輸出的結(jié)果是sizeof(short),即2。
4.數(shù)組做sizeof的參數(shù)不退化,傳遞給strlen就退化為指針了。
5.大部分編譯程序 在編譯的時候就把sizeof計算過了 是類型或是變量的長度這就是sizeof(x)可以用來定義數(shù)組維數(shù)的原因
char str[20]="0123456789";
int a=strlen(str); //a=10;
int b=sizeof(str); //而b=20;
6.strlen的結(jié)果要在運行的時候才能計算出來,時用來計算字符串的長度,不是類型占內(nèi)存的大小。
7.sizeof后如果是類型必須加括弧,如果是變量名可以不加括弧。這是因為sizeof是個操作符不是個函數(shù)。
8.當(dāng)適用了于一個結(jié)構(gòu)類型時或變量,sizeof返回實際的大小,
當(dāng)適用一靜態(tài)地空間數(shù)組,sizeof歸還全部數(shù)組的尺寸。
sizeof操作符不能返回動態(tài)地被分派了的數(shù)組或外部的數(shù)組的尺寸
3.do while和while do的區(qū)別?
while語句是先測試條件再執(zhí)行語句,條件不符后終止
do while語句是先執(zhí)行語句再測試條件,條件不符后終止,所以do while循環(huán)至少執(zhí)行一次
15、const意味著”只讀”,關(guān)鍵字const什么含義?下面的聲明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作?用是?一樣,a是?一個常整型數(shù)。第三個意味著a是?一個指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個意思a是?一個指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后?一個意味著a是?一個指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時
指針也是不可修改的)。
欲阻?止?一個變量被改變,可以使?用const關(guān)鍵字。在定義該const變量時,通常需要對它進?行初
始化,因為以后就沒有機會再去改變它了;
(2)對指針來說,可以指定指針本?身為const,也可以指定指針?biāo)傅臄?shù)據(jù)為const,或?二者同時指
定為const;
(3)在?一個函數(shù)聲明中,const可以修飾形參,表明它是?一個輸?入?yún)?shù),在函數(shù)內(nèi)部不
能改變其值;
(4)對于類的成員函數(shù),若指定其為const類型,則表明其是?一個常函數(shù),不能修改類的成員變量;
(5)對于類的成員函數(shù),有時候必須指定其返回值為const類型,以使得其返回值不 為“左值”。
4.關(guān)鍵字volatile有什么含義?
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設(shè)這個變量的值了。精確地說就是,優(yōu)化器在用到
這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
? 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
? 一個中斷服務(wù)子程序中會訪問到的非自動變量(Non-automatic variables)
? 多線程應(yīng)用中被幾個任務(wù)共享的變量
? 一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
? 一個指針可以是volatile嗎?解釋為什么。
下面是答案:
? 是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應(yīng)該試圖去修改它。
? 是的。盡管這并不很常見。一個例子是當(dāng)一個中服務(wù)子程序修該一個指向一個buffer的指針時。
6、nil,NSNULL,NULL區(qū)別
nil定義一個實例為空,指向oc中對象的空指針.是對objective c id對象賦空值,對于objective c集合類對象 比如數(shù)組對象,字典對象,當(dāng)我們不需要再使用他們的時候,對他們release的同時最好也把他們賦值為nil,這樣確保安全性,如果不賦值nil,可能導(dǎo)致程序崩潰
NSNull類定義了一個單例對象用于表示集合對象的空值
集合對象無法包含nil作為其具體值,如NSArray、NSSet和NSDictionary。相應(yīng)地,nil值用一個特定的對象NSNull來表示。NSNull提供了一個單一實例用于表示對象屬性中的的nil值。默認的實現(xiàn)方法中,dictionaryWithValuesForKeys:和setValuesForKeysWithDictionary:自動地將NSNull和nil相互轉(zhuǎn)換,因此您的對象不需要進行NSNull的測試操作。
NULL可以用在C語言的各種指針上,在Objective-C里,nil對象被設(shè)計來跟NULL空指針關(guān)聯(lián)的。他們的區(qū)別就是nil是一個對象,而NULL只是一個值。而且我們對于nil調(diào)用方法,不會產(chǎn)生crash或者拋出異常。
8、NSDictionary和NSMutableDictionary的區(qū)別,是有序還是無序(追問:可以排序嗎?如何排序,最好代碼實現(xiàn))
NSDictionary是不可變的對象,NSMutableDictionary是可變對象,可以進行添加和刪除操作。
是無序的
排序的話可以這樣做:
1.獲取所有的key
NSArray *myKeys = [myDictionary allKeys];
2.對key進行排序
NSArray *sortedKeys = [myKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
3.按照key獲取對象
id firstObject = [myDictionary objectForKey: [sortedKeys objectAtIndex:0]];
keysSortedByValueUsingSelector/keysSortedByValueUsingComparator通過使用指定SEL或NSComarator來對allKeys進行排序,然后通過objectsForKeys取出排序后的鍵-值對。
5、內(nèi)存中的棧和堆的區(qū)別是什么?哪些數(shù)據(jù)在棧上哪些數(shù)據(jù)在堆上?
(1)管理方式:對于棧來講,是由編譯器自動管理,無需我們手工控制;對于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。
(2)申請大小:能從棧獲得的空間較小,堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。堆的大小受限于計算機系統(tǒng)中 有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。
(3)碎片問題:對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。 對于棧來講,則不會存在這個問題,因為棧是先進后出的隊列,他們是如此的一一對應(yīng),以至于永遠都不可能有一個內(nèi)存塊 從棧中間彈出
(4)分配方式:堆都是動態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配。靜態(tài)分配是編譯器完成 的,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進行分配,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器 進行釋放,無需我們手工實現(xiàn)。
(5)分配效率:棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧 都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機制是很復(fù)雜的。
在函數(shù)體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內(nèi)存的函數(shù)分配得到的就是在堆上。
8)淺復(fù)制和深復(fù)制的區(qū)別
淺層復(fù)制:只復(fù)制指向?qū)ο蟮闹羔?而不復(fù)制引用對象本身。
深層復(fù)制:復(fù)制引用對象本身。
意思就是說我有個A對象,復(fù)制一份后得到A_copy對象后,對于淺復(fù)制來說,A和A_copy指向的是同一 個內(nèi)存資源,復(fù)制的只不過是是一個指針,對象本身資源 還是只有一份,那如果我們對A_copy執(zhí)行了修改操作,那么發(fā)現(xiàn)A引用的對象同樣被修改,這其實違背 了我們復(fù)制拷貝的一個思想。深復(fù)制就好理解了,內(nèi)存中存在了
兩份獨立對象本身。
用網(wǎng)上一哥們通俗的話將就是:
淺復(fù)制好比你和你的影子,你完蛋,你的影子也完蛋
深復(fù)制好比你和你的克隆人,你完蛋,你的克隆人還活著。
深淺拷貝前提是:是實現(xiàn)NSCopying或者NSMutableCopying協(xié)議。
淺拷貝只是復(fù)制對象本身,對象的屬性和包含的對象不做復(fù)制。
深拷貝則對對象本身復(fù)制,同時對對象的屬性也進行復(fù)制。
深淺拷貝的本質(zhì)區(qū)別是對象或者對象屬性的內(nèi)存地址是否一樣,一樣則為淺拷貝,不一樣則為深拷貝。
Foundation框架支持復(fù)制的類,默認是淺拷貝。其中對Foundation中不可變的對象進行copy時作用相當(dāng)于retain。
而如果是mutablecopy時,無論對象是否可變,副本是可變的,并且實現(xiàn)了真正意義上的copy。如果對可變對象進行copy,
副本對象是不可變的,同樣是真正意義上的copy。
12readwrite,readonly,assign,retain,copy,nonatomic屬性的作?
@property是?一個屬性訪問聲明,擴號內(nèi)?支持以下?幾個屬性: 1,getter=getterName,setter=setterName,設(shè)置setter與getter的?方法名2,readwrite,readonly,設(shè)置可供訪問級別2,assign,setter?方法直接賦值,不進?行任何retain操作,為了解決原類型與環(huán)循引?用問題3,retain,setter?方法對參數(shù)進?行release舊值再retain新值,所有實現(xiàn)都是這個順序(CC上有相關(guān)資料) 4,copy,setter?方法進?行Copy操作,與retain處理流程?一樣,先舊值release,再Copy出新的對象, retainCount為1。這是為了減少對上下?文的依賴?而引?入的機制。copy是在你不希望a和b共享?一塊內(nèi)存時會使?用到。a和b各?自有?自?己的內(nèi)存。5,nonatomic,?非原?子性訪問,不加同步,多線程并發(fā)訪問會提?高性能。注意,如果不加此屬性,則 默認是兩個訪問?方法都為原?子型事務(wù)訪問。鎖被加到所屬對象實例級(我是這么理解的...)。atomic和nonatomic?用來決定編譯器?生成的getter和setter是否為原?子操作。在多線程環(huán)境下,原?子操作 是必要的,否則有可能引起錯 誤的結(jié)果。
property中屬性retain,copy,assgin的含義分別是什么?有什么區(qū)別?將其轉(zhuǎn)換成get/set方法怎么做?有什么注意事項?
assign: 簡單賦值,不更改索引計數(shù)(Reference Counting)。
copy: 建立一個索引計數(shù)為1的對象,然后釋放舊對象
retain:釋放舊的對象,將舊對象的值賦予輸入對象,再提高輸入對象的索引計數(shù)為1
使用assign:對基礎(chǔ)數(shù)據(jù)類型 (NSInteger,CGFloat)和C數(shù)據(jù)類型(int, float, double, char,等等)
使用copy: 對NSString
使用retain: 對其他NSObject和其子類
retain表示持有特性,setter方法將傳入?yún)?shù)先保留,再賦值,傳入?yún)?shù)的retaincount會+1;
- (void)setInstance:(id)instance{
if (_instance != instance) {
[_instance release];
_instance = [instance retain];
}
}
copy表示賦值特性,setter方法將傳入對象復(fù)制一份;需要完全一份新的變量時。
- (void)setInstance:(id)instance{
if (_instance != instance) {
[_instance release];
_instance = [instance copy];
}
}
assign是賦值特性,setter方法將傳入?yún)?shù)賦值給實例變量;僅設(shè)置變量時;
- (void)setInstance:(id)instance{
if (_instance != instance) {
_instance = instance;
}
}
7、setValue和setObject區(qū)別
在使用NSMutableDictionary的時候經(jīng)常會使用setValue forKey與setObject forKey,他們經(jīng)常是可以交互使用的,代碼中經(jīng)常每一種的使用都有。
他們2者的區(qū)別就是:
1、setObject:forkey:中value是不能夠為nil的,不然會報錯。
setValue:forKey:中value能夠為nil,但是當(dāng)value為nil的時候,會自動調(diào)用removeObject:forKey方法
2、setValue:forKey:中key的參數(shù)只能夠是NSString類型,而setObject:forKey:的可以是任何類型
3、setObject:forKey:方法NSMutabledictionary特有的,而
setValue:forKey:方法是KVC(鍵-值編碼)的主要方法。
、類別和擴展
8、聲明并實現(xiàn)一個cotegory為UIView添加addNewView方法
.h
@interface UIView (AddNewView)
- (void)addNewView:(UIView *)view;
@end
.m
#import "UIView+AddNewView.h"
@implementation UIView (AddNewView)
- (void)addNewView:(UIView *)view
{
[self addSubview:view];
}
@end
9)類別和類擴展的區(qū)別
category和extensions的不同在于后者可以添加屬性。另外后者添加的?方法是必須要實現(xiàn)的。extensions可以認為是?一個私有的Category。
9.原子(atomic)跟非原子(non-atomic)屬性有什么區(qū)別?
①. atomic提供多線程安全。是防止在寫未完成的時候被另外一個線程讀取,造成數(shù)據(jù)錯誤
②. non-atomic:在自己管理內(nèi)存的環(huán)境中,解析的訪問器保留并自動釋放返回的值,如果指定了nonatomic,那么訪問器只是簡單地返回這個值。
12.類別的作用?繼承和類別在實現(xiàn)中有何區(qū)別?
category可以在不獲悉,不改變原來代碼的情況下往里面添加新的方法,只能添加,不能刪除修改。
并且如果類別和原來類中的方法產(chǎn)生名稱沖突,則類別將覆蓋原來的方法,因為類別具有更高的優(yōu)先級。
類別主要有3個作用:
(1)將類的實現(xiàn)分散到多個不同文件或多個不同框架中。
(2)創(chuàng)建對私有方法的前向引用。
(3)向?qū)ο筇砑臃钦絽f(xié)議。
繼承可以增加,修改或者刪除方法,并且可以增加屬性。
14.什么是KVO和KVC?
kvc:鍵 – 值編碼是一種間接訪問對象的屬性使用字符串來標(biāo)識屬性,而不是通過調(diào)用存取方法,直接或通過實例變量訪問的機制。
很多情況下可以簡化程序代碼。apple文檔其實給了一個很好的例子。
kvo:鍵值觀察機制,他提供了觀察某一屬性變化的方法,極大的簡化了代碼。
具體用看到嗯哼用到過的一個地方是對于按鈕點擊變化狀態(tài)的的監(jiān)控。
比如我自定義的一個button
[self addObserver:self forKeyPath:@"highlighted" options:0 context:nil];
#pragma mark KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"highlighted"] ) {
[self setNeedsDisplay];
}
}
對于系統(tǒng)是根據(jù)keypath去取的到相應(yīng)的值發(fā)生改變,理論上來說是和kvc機制的道理是一樣的。
對于kvc機制如何通過key尋找到value:
“當(dāng)通過KVC調(diào)用對象時,比如:[self valueForKey:@”someKey”]時,程序會自動試圖通過幾種不同的方式解析這個調(diào)用。首先查找對象是否帶有someKey這個方法,如果沒找到,會繼續(xù)查找對象是否帶有someKey這個實例變量(iVar),如果還沒有找到,程序會繼續(xù)試圖調(diào)用-(id) valueForUndefinedKey:這個方法。如果這個方法還是沒有被實現(xiàn)的話,程序會拋出一個NSUndefinedKeyException異常錯誤。(cocoachina.com注:Key-Value Coding查找方法的時候,不僅僅會查找someKey這個方法,還會查找getsomeKey這個方法,前面加一個get,或者_someKey以及_getsomeKey這幾種形式。同時,查找實例變量的時候也會不僅僅查找someKey這個變量,也會查找_someKey這個變量是否存在。)設(shè)計valueForUndefinedKey:方法的主要目的是當(dāng)你使用-(id)valueForKey方法從對象中請求值時,對象能夠在錯誤發(fā)生前,有最后的機會響應(yīng)這個請求。這樣做有很多好處,下面的兩個例子說明了這樣做的好處?!皝碇羉ocoa,這個說法應(yīng)該挺有道理。因為我們知道button卻是存在一個highlighted實例變量.因此為何上面我們只是add一個相關(guān)的keypath就行了,可以按照kvc查找的邏輯理解,就說的過去了。
15.我們說的OC是動態(tài)運行時語言是什么意思?
多態(tài)。 主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運行時。
這個問題其實淺涉及到兩個概念,運行時和多態(tài)。
簡單來說,運行時機制使我們直到運行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法。
多態(tài):不同對象以自己的方式響應(yīng)相同的消息的能力叫做多態(tài)。意思就是假設(shè)生物類(life)都用有一個相同的方法-eat;
那人類屬于生物,豬也屬于生物,都繼承了life后,實現(xiàn)各自的eat,但是調(diào)用是我們只需調(diào)用各自的eat方法。
也就是不同的對象以自己的方式響應(yīng)了相同的消息(響應(yīng)了eat這個選擇器)。因此也可以說,運行時機制是多態(tài)的基礎(chǔ)。
18.類變量的@protected ,@private,@public,@package,聲明各有什么含義?(OC第二講 ?實例變量可見度與方法)
上面的幾個聲明表明的時類成員的作用域,@private作用范圍只能在自身類(外界既不可訪問,又不能繼承);@protected作用范圍在自身類和子類,如果什么都不加修飾,默認是@protected(外界不可訪問,但是可以繼承);@public作用范圍最大,可以在任何地方被訪問(外界即可訪問,又可以繼承);@package作用范圍在某個框架內(nèi)
線程是什么?進程是什么?二者有什么區(qū)別和聯(lián)系? (UI第二十二講 多線程編程)
線程是CPU獨立運行和獨立調(diào)度的基本單位(可以理解為一個進程中執(zhí)行的代碼片段),進程是資源分配的基本單位(進程是一塊包含了某些資源的內(nèi)存區(qū)域)。進程是線程的容器,真正完成代碼執(zhí)行的是線程,而進程則作為線程的執(zhí)行環(huán)境。一個程序至少包含一個進程,一個進程至少包含一個線程,一個進程中的多個線程共享當(dāng)前進程所擁有的資源。
32. What is lazy loading?
在使用的時候才去初始化,比如UITableViewCell的imageView屬性,懶加載對象的創(chuàng)建是在getter方法里面進行創(chuàng)建的。
34. isKindOfClass、isMemberOfClass作用分別是什么?
-(BOOL) isKindOfClass: classObj判斷是否是這個類或者是這個類子類的實例
-(BOOL) isMemberOfClass: classObj判斷是否是這個類的實例
OC里怎么實現(xiàn)多繼承
OC沒有多繼承。用協(xié)議實現(xiàn)多繼承,把協(xié)議中的方法在實現(xiàn)的類 中重寫C++有多繼承,多繼承其實用好了很方便。C++多繼承很容易出現(xiàn) 二一性比如:
類A { int a;}
類B:類A,類C:類A
類D : B, C那么int a到底是繼承自誰的單繼承
60.使用圓角效果時為什么會出現(xiàn)卡頓效果,如何解決這個問題?
不要在滾動視圖使用cornerRadius或者mask,添加self.layer.shouldRasterize = YES; ?self.layer.rasterizationScale = [UIScreen mainScreen].scale;
采取預(yù)先生成圓角圖片,并緩存起來這個方法才是比較好的手段。預(yù)處理圓角圖片可以在后臺處理,處理完畢后緩存起來,再在主線程顯示,這就避免了不必要的離屏渲染了。
在網(wǎng)絡(luò)請求中如何提高性能
在網(wǎng)絡(luò)請求中如何保證安全性
iOS在數(shù)據(jù)傳輸過程中有數(shù)據(jù)泄露的可能,客戶端在進行網(wǎng)絡(luò)請求時一般都采用加密的方式來保證數(shù)據(jù)的安全性,在iOS9以后,蘋果為了保護數(shù)據(jù)的安全性,使用了Https協(xié)議。
86.#define和const定義的變量,有什么區(qū)別什么情況下會出現(xiàn)內(nèi)存的循環(huán)引用block中的weak self,是任何時候都需要加的么?
答案:
1.宏:只是在預(yù)處理器里進行文本替換,沒有類型,不做任何類型檢查,編譯器可以對相同的字符串進行優(yōu)化。只保存一份到.rodata段。甚至有相同后綴的字符串也可以優(yōu)化,你可以用GCC編譯測試,"Hello world"與"world"兩個字符串,只存儲前面一個。取的時候只需要給前面和中間的地址,如果是整形、浮點型會有多份拷貝,但這些數(shù)寫在指令中。占的只是代碼段而已,大量用宏會導(dǎo)致二進制文件變大.常量:共享一塊內(nèi)存空間,就算項目中N處用到,也不會分配N塊內(nèi)存空間,可以根據(jù)const修飾的位置設(shè)定能否修改,在編譯階段會執(zhí)行類型檢查
2.(1)計時器NSTimer
一方面,NSTimer經(jīng)常會被作為某個類的成員變量,而NSTimer初始化時要指定self為target,容易造成循環(huán)引用。 另一方面,若timer一直處于validate的狀態(tài),則其引用計數(shù)將始終大于0.
(2)block
block在copy時都會對block內(nèi)部用到的對象進行強引用(ARC)或者retainCount增1(非ARC)。在ARC與非ARC環(huán)境下對block使用不當(dāng)都會引起循環(huán)引用問題,一般表現(xiàn)為,某個類將block作為自己的屬性變量,然后該類在block的方法體里面又使用了該類本身,簡單說就是self.someBlock = ^(Type var){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的這種循環(huán)引用會被編譯器捕捉到并及時提醒。
(3)委托delegate
在委托問題上出現(xiàn)循環(huán)引用問題已經(jīng)是老生常談了,本文也不再細講,規(guī)避該問題的殺手锏也是簡單到哭,一字訣:聲明delegate時請用assign(MRC)或者weak(ARC),千萬別手賤玩一下retain或者strong,畢竟這基本逃不掉循環(huán)引用了!
3.使用方將self或成員變量加入block之前要先將self變?yōu)開_weak
Configuration中,debug和release的區(qū)別是什么?
Release是發(fā)行版本,比Debug版本有一些優(yōu)化,文件比Debug文件小Debug是調(diào)試版本,Debug和Release調(diào)用兩個不同的底層庫。
一、"Debug是調(diào)試版本,包括的程序信息更多"
二、只有DEBUG版的程序才能設(shè)置斷點、單步執(zhí)行、使用TRACE/ASSERT等調(diào)試輸出語句。
三、REALEASE不包含任何調(diào)試信息,所以體積小、運行速度快。
、Initilize和load的區(qū)別
http://www.cnblogs.com/ider/archive/2012/09/29/objective_c_load_vs_initialize.html
1.怎么修改只讀(readonly)屬性的變量
使用KVC可以修改,kvc就是鍵值編碼(key-value),可以通過指定的key獲得想要的值value。而不是通過調(diào)用Setter、Getter方法訪問。
什么情況使?用weak關(guān)鍵字,相?比assign有什么不同?在ARC中,防?止在有可能出現(xiàn)循環(huán)引?用的時候使?用weak關(guān)鍵字,?比如:block。assign可以修飾基本數(shù)據(jù)類型和C數(shù)據(jù)類型,也就是簡單的賦值,?而weak只能修飾OC對象類型。
1)在ARC中,在有可能出現(xiàn)循環(huán)引?用的時候,往往要通過讓其中?一端使?用weak來解決,?比如:delegate代理屬性
2)自身已經(jīng)對它進?行?一次強引?用,沒有必要再強引?用?一次,此時也會使?用weak,?自定義IBOutlet控件屬性?一般也使?用weak;當(dāng)然,也可以使?用strong。在下文也有論述:《IBOutlet連出來的視圖屬性為什么可以被設(shè)置成weak?》不同點:
1)weak此特質(zhì)表明該屬性定義了?一種“?非擁有關(guān)系”(nonowning relationship)。為這種屬性設(shè)置新值時,設(shè)置?方法既不保留新值,也不釋放舊值。此特質(zhì)同assign類似,然?而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。?而assign的“設(shè)置?方法”只會執(zhí)?行針對“純量類型”(scalar type,例如CGFloat或NSlnteger等)的簡單賦值操作。
2) assign可以用非OC對象,而weak必須用于OC對象
3、copy和mutablecopy的區(qū)別copy返回?一個不可變對象,mutableCopy是創(chuàng)建?一個新的可變對象,并初始化源
對象的值,引?用計數(shù)為1。
1. mutableCopy創(chuàng)建?一個新的可變對象,并初始化為原對象的值,新對象的引用計數(shù)為1;
2. copy返回?一個不可變對象。分兩種情況:(1)若原對象是不可變對象,那么返回原對象,并將其引用計數(shù)加1 ;(2)若原對象是可變對象,那么創(chuàng)建?一個新的不可變對象,并初始化為原對象的值,新對象的引用計數(shù)為1。
@synthesize和@dynamic的區(qū)別?
@synthesize就是?自動?生成屬性的setter和getter?方法。
@dynamic就是要告訴編譯器,?自?己實現(xiàn)?方法。
object-c為了讓java的開發(fā)者習(xí)慣使?用.的操作,所以可以將接??口類中的變量使?用@property來聲明屬性。但是在.h中聲明的屬性,必須在.m中使?用@synthesize或者@dynamic來實現(xiàn)(傳?言,在最近出的ios6中這步已經(jīng)省了),否則屬性不可?用。熟悉object-c語法的都知道@synthesize實際的意義就是?自動?生成屬性的setter和getter?方法。
@dynamic就是要告訴編譯器,代碼中?用@dynamic修飾的屬性,其getter和setter?方法會在程序運?行的時候或者?用其他?方式動態(tài)綁定,以便讓編譯器通過編譯。其主要的作?用就是?用在NSManagerObject對象的屬性聲明上,由于此類對象的屬性?一般是從Core Data的屬性中?生成的,core data框架會在程序運?行的時候為此類屬性?生成getter和setter?方法,但是如果不是NSManagerObject的?子類,就必須?手動實現(xiàn)setter和getter?方法,否則將出現(xiàn)野指針;
5、__block和__weak修飾符的區(qū)別
1.__block不管是ARC還是MRC模式下都可以使用,可以修飾對象,還可以修飾基本數(shù)據(jù)類型。
2.__weak只能在ARC模式下使用,也只能修飾對象(NSString),不能修飾基本數(shù)據(jù)類型(int)。
3.__block對象可以在block中被重新賦值,__weak不可以。
PS:__unsafe_unretained修飾符可以被視為iOS SDK 4.3以前版本的__weak的替代品,不過不會被自動置空為nil。所以盡可能不要使用這個修飾符。
6.在什么情況下會用到weak這個關(guān)鍵字,作用是什么?
適用于OC對象,作用和非ARC中的assign作用相同,修飾的成員變量為弱指針類型
IBOutlet的屬性一般可以設(shè)為weak是因為它已經(jīng)被view引用了,除非view被釋放,否則IBOutlet的屬性也不會被釋放,另外IBOutlet屬性的生命周期和view應(yīng)該是一致的,所以IBOutlet屬性一般設(shè)為weak。
4、隊列和棧的區(qū)別是什么?
1.隊列先進先出,棧先進后出。
2.對插入和刪除操作的"限定"。 棧是限定只能在表的一端進行插入和刪除操作的線性表。 隊列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。 從"數(shù)據(jù)結(jié)構(gòu)"的角度看,它們都是線性結(jié)構(gòu),即數(shù)據(jù)元素之間的關(guān)系相同。但它們是完全不同的數(shù)據(jù)類型。除了它們各自的基本操作集不同外,主要區(qū)別是對插入和刪除操作的"限定"。 棧和隊列是在程序設(shè)計中被廣泛使用的兩種線性數(shù)據(jù)結(jié)構(gòu),它們的特點在于基本操作的特殊性,棧必須按"后進先出"的規(guī)則進行操作,而隊列必須按"先進先出"的規(guī)則進行操作。和線性表相比,它們的插入和刪除操作受更多的約束和限定,故又稱為限定性的線性表結(jié)構(gòu)。
3.遍歷數(shù)據(jù)速度不同。棧只能從頭部取數(shù)據(jù) 也就最先放入的需要遍歷整個棧最后才能取出來,而且在遍歷數(shù)據(jù)的時候還得為數(shù)據(jù)開辟臨時空間,保持數(shù)據(jù)在遍歷前的一致性隊列怎不同,他基于地址指針進行遍歷,而且可以從頭或尾部開始遍歷,但不能同時遍歷,無需開辟臨時空間,因為在遍歷的過程中不影像數(shù)據(jù)結(jié)構(gòu),速度要快的多。
8、NSDictionary和NSMutableDictionary的區(qū)別,是有序還是無序(追問:可以排序嗎?如何排序,最好代碼實現(xiàn))
NSDictionary是不可變的對象,NSMutableDictionary是可變對象,可以進行添加和刪除操作。
是無序的
排序的話可以這樣做:
1.獲取所有的key
NSArray *myKeys = [myDictionary allKeys];
2.對key進行排序
NSArray *sortedKeys = [myKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
3.按照key獲取對象
id firstObject = [myDictionary objectForKey: [sortedKeys objectAtIndex:0]];
keysSortedByValueUsingSelector/keysSortedByValueUsingComparator通過使用指定SEL或NSComarator來對allKeys進行排序,然后通過objectsForKeys取出排序后的鍵-值對。
9、推送通知和本地通知的區(qū)別
本地推送和遠程推送的區(qū)別是本地推送一般是用來實現(xiàn)鬧鐘或者生日提醒的功能.遠程推送是為了實現(xiàn)服務(wù)器想要把及時的消息在某個特定的時間發(fā)送給用戶的手段.
本地推送不需要申請推送證書,遠程推送需要先申請證書然后再去實現(xiàn)證書.
13.AVPlayer ?AVAudioPlayer ?MediaPlayer的不同
AVAudioPlayer:使用簡單方便,但只能播放本地音頻,不支持流媒體播放
AVPlayer: iOS4.0以后,可以使用AVPlayer播放本地音頻和支持流媒體播放,但是提供接口較少,處理音頻不夠靈活
MediaPlayer :是用來處理網(wǎng)絡(luò)上的視頻文件的播放的系統(tǒng)類.
24XML、JSON解析;
(1)區(qū)別:
(1)可讀性方面:基本相同,xml的可讀性比較好
(2)可擴展性方面:都具有很好的擴展性
(3)編碼難度方面:相對而言:JSON的編碼比較容易
(4)解碼難度:json的解碼難度基本為零,xml需要考慮子節(jié)點和父節(jié)點
(5)數(shù)據(jù)體積方面:json相對于xml來講,數(shù)據(jù)體積小,傳遞的速度跟快些
(6)數(shù)據(jù)交互方面:json與JavaScript的交互更加方面,更容易解析處理,更好的數(shù)據(jù)交互
(7)數(shù)據(jù)描述方面:xml對數(shù)據(jù)描述性比較好
(8)傳輸速度方面:json的速度遠遠快于xml
(2)
1)JSON底層原理:
遍歷字符串中的字符,最終根據(jù)格式規(guī)定的特殊字符,比如{}號,[]號, :號等進行區(qū)分,{}號是一個字典的開始,[]號是一個數(shù)組的開始, :號是字典的鍵和值的分水嶺,最終乃是將json數(shù)據(jù)轉(zhuǎn)化為字典,字典中值可能是字典,數(shù)
組,或字符串而已。
2)XML底層原理:XML解析常用的解析方法有兩種:DOM解析和SAX解析。DOM采用建立樹形結(jié)構(gòu)的方式訪問XML文檔,而
SAX采用的事件模型。
DOM解析把XML文檔轉(zhuǎn)化為一個包含其內(nèi)容的樹,并可以對樹進行遍歷。使用DOM解析器的時候需要處理整個XML文檔,所以對性能和內(nèi)存的要求比較高。SAX在解析XML文檔的時候可以觸發(fā)一系列的事件,當(dāng)發(fā)現(xiàn)給定的tag的時候,它可以激活一個回調(diào)方法,告訴該方法制定的標(biāo)簽已經(jīng)找到。SAX對內(nèi)存的要求通常會比較低,因為它讓開發(fā)人員自己來決定所要處理的tag。特別是當(dāng)開發(fā)人員只需要處理文檔中所包含的部分數(shù)據(jù)時,SAX這種擴展能力得到了更好的體現(xiàn)。
3)SAX與DOM的區(qū)別:
1、SAX處理的優(yōu)點非常類似于流媒體的優(yōu)點。分析能夠立即開始,而不是等待所有的數(shù)據(jù)被處理。而且由于應(yīng)用程序只是在讀取數(shù)據(jù)時檢查數(shù)據(jù),因此不需要將數(shù)據(jù)存儲在內(nèi)存中。這對于大型文檔來說是個巨大的優(yōu)點。事實上,應(yīng)用程序甚至不必解析整個文檔;它可以在某個條件得到滿足時停止解析。一般來說,SAX還比它的替代者DOM快許多。另一方面,由于應(yīng)用程序沒有以任何方式存儲數(shù)據(jù),使用SAX來更改數(shù)據(jù)或在數(shù)據(jù)流中往后移是不可能的。
2、DOM以及廣義的基于樹的處理具有幾個優(yōu)點。首先,由于樹在內(nèi)存中是持久的,因此可以修改它以便應(yīng)用程序能對數(shù)據(jù)和結(jié)構(gòu)作出更改。它還可以在任何時候在樹中上下導(dǎo)航,而不是像SAX那樣是一次性的處理。DOM使用起來也要簡單得多。另一方面,在內(nèi)存中構(gòu)造這樣的樹涉及大量的開銷。大型文件完全占用系統(tǒng)內(nèi)存容量的情況并不鮮見。此外,創(chuàng)建一棵DOM樹可能是一個緩慢的過程。
11.方法(method)和選擇器有何不同?
NSDictionary是不可變的對象,NSMutableDictionary是可變對象,可以進行添加和刪除操作。
是無序的
排序的話可以這樣做:
1.獲取所有的key
NSArray *myKeys = [myDictionary allKeys];
2.對key進行排序
NSArray *sortedKeys = [myKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
3.按照key獲取對象
id firstObject = [myDictionary objectForKey: [sortedKeys objectAtIndex:0]];
keysSortedByValueUsingSelector/keysSortedByValueUsingComparator通過使用指定SEL或NSComarator來對allKeys進行排序,然后通過objectsForKeys取出排序后的鍵-值對。
1.bounds和frame的區(qū)別?bound的大小改變frame改變嗎?
bounds相當(dāng)于是一個視圖的坐標(biāo),一個視圖的frame是相對于父視圖的bounds,改變bounds并不會影響本視圖的frame,但是會對子視圖的布局造成影響
24.說說響應(yīng)鏈
當(dāng)事件發(fā)生的時候,響應(yīng)鏈?zhǔn)紫缺话l(fā)送給第一個響應(yīng)者(往往是事件發(fā)生的視圖,也就是用戶觸摸屏幕的地方)。事件將沿著響應(yīng)者鏈一直向下傳遞,直到被接受并作出處理。一般來說,第一響應(yīng)這是個視圖對象或者其子類,當(dāng)其被觸摸后事件就交由它處理,如果他不處理,時間就會被傳遞給視圖控制器對象UIViewController(如果存在),然后是它的父視圖對象(superview),以此類推直到頂層視圖。接下來會沿著頂層視圖(topview)到窗口(UIwindow對象)再到程序的(UIApplication對象),如果整個過程都沒有響應(yīng)這個事件,則該事件被丟棄,一般情況下,在響應(yīng)鏈中只要有對象處理事件,事件就會被傳遞典型的響應(yīng)路線圖如:First Responser -> The Window ->The Applicationn -> App Delegate
4、drawInRect
在運行到drawInRect:withFont: lineBreakMode:alignment:,出現(xiàn)提示iOS7棄用這個方法,使用drawInRect:withAttributes:的方法的提示。但是這個方法在說明的時候,寫的很簡單,第二個參數(shù)是一個字典,根本就搞不明白是什么東西。我在查了不少資料后,才翻到字典里面能存放的東西:NSAttributedString,里面竟然可以使用21組鍵值對,功能強大了不少。NSParagraphStyleAttributeName這個key就能代替iOS6上的那個棄用方法,不過值必須得是NSMutableParagraphStyle,不然不能自定義
5、NSAttributedString屬性字符串
NSAttributedString管理一個字符串,以及與該字符串中的單個字符或某些范圍的字符串相關(guān)的屬性。比如這個字符串“我愛北京天安門”,“我”跟其他字符的顏色不一樣,而“北京”與其他的字體和大小不一樣,等等。NSAttributedString就是用來存儲這些信息的,具體實現(xiàn)時,NSAttributedString維護了一個NSString,用來保存最原始的字符串,另有一個NSDictionary用來保存各個子串/字符的屬性。
學(xué)習(xí)鏈接:http://blog.csdn.net/zhangao0086/article/details/7616385
UIScrollview重用機制
事件處理看過了,就要考慮scrollView如何重用內(nèi)存的,下面寫了一個例子模仿UITableView的重用的思想,這里只是模仿,至于蘋果公司怎么實現(xiàn)這種重用的,他們應(yīng)該有更好的方法。
這里的例子是在scrollView上放置4個2排2列的視圖,但是內(nèi)存中只占用6個視圖的內(nèi)存空間。當(dāng)scrollView滾動的時候,通過不停的重用之前視圖的內(nèi)存空間,從而達到節(jié)省內(nèi)存的效果。重用的方法如下:
1.如果scrollView向下面滾動,一旦一排視圖滾出了可視范圍,就改變滾動出去的那個view在scrollView中的frame,也就是改變位置到達末尾,達到重用的效果。
2.如果scrollView向上面滾動,一旦最末排的視圖view滾出了可視范圍,就改變滾動出去的那個view在scrollView中的frame,移動到最前面。
8、描述什么是面向?qū)ο螅?/p>
面向?qū)ο?
把數(shù)據(jù)及對數(shù)據(jù)的操作方法放在一起,作為一個相互依存的整體——對象。對同類對象抽象出其共性,形成類。類中的大多數(shù)數(shù)據(jù),只能用本類的方法進行處理。類通過一個簡單的外部接口與外界發(fā)生關(guān)系,對象與對象之間通過消息進行通信。程序流程由用戶在使用中決定。
1、UITableView的執(zhí)行流程是怎么樣的?
第一輪:
1、numberOfSectionsInTableView:假如section=2,此函數(shù)只執(zhí)行一次,假如section=0,下面函數(shù)不執(zhí)行,默認為1
2、heightForHeaderInSection,執(zhí)行兩次,此函數(shù)執(zhí)行次數(shù)為section數(shù)目
3、heightForFooterInSection,函數(shù)屬性同上,執(zhí)行兩次
4、numberOfRowsInSection,此方法執(zhí)行一次
5、heightForHeaderInSection,此方法執(zhí)行了兩次,我其實有點困惑為什么這里還要調(diào)用這個方法
6、heightForFooterInSection,此方法執(zhí)行兩次,
7、numberOfRowsInSection,執(zhí)行一次
8、heightForRowAtIndexPath,行高,先執(zhí)行section=0,對應(yīng)的row次數(shù)
第二輪:
1、numberOfSectionsInTableView,一次
2、heightForHeaderInSection,section次數(shù)
3、heightForFooterInSection,section次數(shù)
4、numberOfRowsInSection,一次
5、heightForHeaderInSection,執(zhí)行section次數(shù)
6、heightForFooterInSection,執(zhí)行section次數(shù)
7、numberOfRowsInSection,執(zhí)行一次
8、heightForRowAtIndexPath,行高,先執(zhí)行一次
9、cellForRowAtIndexPath
10、willDisplayCell
然后8、9、10依次執(zhí)行直到所有的cell被描畫完畢