1、category 和 extension 的區(qū)別
分類有名字,類擴(kuò)展沒有分類名字,是一種特殊的分類
分類只能擴(kuò)展方法(屬性僅僅是聲明,并沒真正實(shí)現(xiàn)),類擴(kuò)展可以擴(kuò)展屬性、成員變量和方法
2、define 和 const常量有什么區(qū)別?
- define 在預(yù)處理階段進(jìn)行替換,const 常量在編譯階段使用
- 宏不做類型檢查,僅僅進(jìn)行替換,const 常量有數(shù)據(jù)類型,會執(zhí)行類型檢查
- define 不能調(diào)試,const 常量可以調(diào)試
- define 定義的常量在替換后運(yùn)行過程中會不斷地占用內(nèi)存,而 const 定義的常量存儲在數(shù)據(jù)段只有一份 copy,效率更高
- define 可以定義一些簡單的函數(shù),const 不可以
3、block和 weak修飾符的區(qū)別?
- __block 不管是 ARC 還是 MRC 模式下都可以使用,可以修飾對象,也可以修飾基本數(shù)據(jù)類型
__weak 只能在 ARC 模式下使用,只能修飾對象(NSString),不能修飾基本數(shù)據(jù)類型
__block 修飾的對象可以在block中被重新賦值,中被重新賦值,__weak 修飾的對象不可以
4、static關(guān)鍵字的作用
函數(shù)(方法)體內(nèi) static 變量的作用范圍為該函數(shù)體,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時仍維持上次的值;在模塊內(nèi)的 static 全局變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問;在模塊內(nèi)的 static 函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個函數(shù)的使用范圍被限制在聲明 它的模塊內(nèi);在類中的 static 成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;在類中的 static 成員函數(shù)屬于整個類所擁有,這個函數(shù)不接收 this 指針,因而只能訪問類的 static 成員變量
5、堆和棧的區(qū)別
從管理方式來講
對于棧來講,是由編譯器自動管理,無需我們手工控制;
對于堆來說,釋放工作由程序員控制,容易產(chǎn)生內(nèi)存泄露(memory leak)
從申請大小大小方面講
棧空間比較小
堆控件比較大
從數(shù)據(jù)存儲方面來講
棧空間中一般存儲基本類型,對象的地址
堆空間一般存放對象本身,block 的 copy 等
6、風(fēng)格糾錯題
修改后的代碼:
typedef NS_ENUM(NSInteger, CYLSex)
{
CYLSexMan,
CYLSexWoman
};
@interface CYLUser : NSObject<NSCopying>
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readwrite) CYLSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age
sex:(CYLSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age
sex:(CYLSex)sex;
@end
7、Objective-C使用什么機(jī)制管理對象內(nèi)存?
MRC 手動引用計(jì)數(shù)
ARC 自動引用計(jì)數(shù),現(xiàn)在通常 ARC
通過 retainCount 的機(jī)制來決定對象是否需要釋放。 每次 runloop 的時候,都會檢查對象的 retainCount,如果 retainCount 為 0,說明該對象沒有地方需要繼續(xù)使用了,可以釋放掉了
8、ARC通過什么方式幫助開發(fā)者管理內(nèi)存?
通過編譯器在編譯的時候,插入類似內(nèi)存管理的代碼
注意:不管是MRC還是ARC都是在編譯期進(jìn)行的,因?yàn)檫\(yùn)行期兩者運(yùn)行結(jié)果無差別。
9、ARC是為了解決什么問題誕生的?
首先解釋 ARC: automatic reference counting 自動引用計(jì)數(shù)
MRC 的缺點(diǎn):
在 MRC 時代當(dāng)我們要釋放一個堆內(nèi)存時,首先要確定指向這個堆空間的指針都被release 了
釋放指針指向的堆空間,首先要確定哪些指針指向同一個堆,這些指針只能釋放一次(MRC 下即誰創(chuàng)建,誰釋放,避免重復(fù)釋放)
模塊化操作時,對象可能被多個模塊創(chuàng)建和使用,不能確定最后由誰去釋放
多線程操作時,不確定哪個線程最后使用完畢
綜上所述,MRC 有諸多缺點(diǎn),很容易造成內(nèi)存泄露和壞內(nèi)存的問題,這時蘋果為盡量解決這個問題,從而誕生了 ARC
10、ARC下還會存在內(nèi)存泄露嗎?
循環(huán)引用會導(dǎo)致內(nèi)存泄露.
Objective-C 對象與 CoreFoundation 對象進(jìn)行橋接的時候如果管理不當(dāng)也會造成內(nèi)存泄露.
CoreFoundation 中的對象不受 ARC 管理,需要開發(fā)者手動釋放
11、什么情況使用 weak關(guān)鍵字,相比 assign有什么不同?
- 首先明白什么情況使用 weak關(guān)鍵字?
- 1.在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如:delegate 代理屬性,代理屬性也可使用 assign
- 2.自身已經(jīng)對它進(jìn)行一次強(qiáng)引用,沒有必要再強(qiáng)引用一次,此時也會使用 weak,自定義
- 3.IBOutlet 控件屬性一般也使用 weak;當(dāng)然,也可以使用 strong,但是建議使用 weak
- weak 和 assign 的不同點(diǎn)
- weak 策略在屬性所指的對象遭到摧毀時,系統(tǒng)會將 weak 修飾的屬性對象的指針指向 nil,在 OC 給 nil 發(fā)消息是不會有什么問題的;如果使用 assign 策略在屬性所指的對象遭到摧毀時,屬性對象指針還指向原來的對象,由于對象已經(jīng)被銷毀,這時候就產(chǎn)生了野指針,如果這時候在給此對象發(fā)送消息,很容造成程序奔潰
- assigin 可以用于修飾非 OC 對象,而 weak 必須用于 OC 對象
12、@property 的本質(zhì)是什么?
@property = ivar(實(shí)例變量) + getter + setter;
@property 其實(shí)就是在編譯階段由編譯器自動幫我們生成 ivar 成員變量,getter 方法,setter 方法
13、ivar、getter、setter是如何生成并添加到這個類中的?
使用“自動合成”( autosynthesis)
這個過程由編譯器在編譯階段執(zhí)行自動合成,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼
除了生成 getter、setter 方法之外,編譯器還要自動向類中添加成員變量(在屬性名前面加下劃線,以此作為實(shí)例變量的名字)
為了搞清屬性是怎么實(shí)現(xiàn)的,反編譯相關(guān)的代碼,他大致生成了五個東西
// 該屬性的“偏移量” (offset),這個偏移量是“硬編碼” (hardcode),表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠(yuǎn)
OBJC_IVAR_$類名$屬性名稱
// 方法對應(yīng)的實(shí)現(xiàn)函數(shù)
setter與 getter
// 成員變量列表
ivar_list
// 方法列表
method_list
// 屬性列表
prop_list
每次增加一個屬性,系統(tǒng)都會在 ivar_list 中添加一個成員變量的描述
在 method_list 中增加 setter 與 getter 方法的描述
在 prop_list 中增加一個屬性的描述
計(jì)算該屬性在對象中的偏移量
然后給出 setter 與 getter 方法對應(yīng)的實(shí)現(xiàn),在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值,為了能夠讀取正確字節(jié)數(shù),系統(tǒng)對象偏移量的指針類型進(jìn)行了類型強(qiáng)轉(zhuǎn)
14、@protocol 和 category 中如何使用
@property
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實(shí)現(xiàn)該屬性
category 使用 @property 也是只會生成 setter 和 getter 方法聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時的兩個函數(shù)
objc_setAssociatedObject
objc_getAssociatedObject
15、@property后面可以有哪些修飾符?
原子性---nonatomic 特質(zhì),如果不寫默認(rèn)情況為 atomic(系統(tǒng)會自動加上同步鎖,影響性能)在 iOS 開發(fā)中盡量指定為 nonatomic,這樣有助于提高程序的性能
讀/寫權(quán)限---readwrite(讀寫)、readooly (只讀)
內(nèi)存管理語義---assign、strong、 weak、unsafe_unretained、copy
方法名---getter=、setter=
@property (nonatomic, getter=isOn) BOOL on;
// setter=<name>這種不常用,也不推薦使用。故不在這里給出
寫法
不常用的:nonnull,null_resettable,nullable
16、使用 atomic一定是線程安全的嗎?
不是,atomic 的本意是指屬性的存取方法是線程安全的,并不保證整個對象是線程安全的。
舉例:聲明一個 NSMutableArray 的原子屬性 stuff,此時 self.stuff 和 self.stuff = othersulf 都是線程安全的。但是,使用[self.stuffobjectAtIndex:index]就不是線程安全的,需要用互斥鎖來保證線程安全性
17、@synthesize 和 @dynamic分別有什么作用
@property 有兩個對應(yīng)的詞,一個是 @synthesize ,一個是@dynamic。如果 @synthesize 和@dynamic 都沒寫,那么默認(rèn)的就是@syntheszie var = _var;
@synthesize 的語義是如果你沒有手動實(shí)現(xiàn) setter 方法和 getter 方法,那么編譯器會自動為你加上這兩個方法
@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(xiàn),不自動生成(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)
假如一個屬性被聲明為@dynamic var,然后你沒有提供@setter 方法和@getter 方法,編譯的時候沒問題,但是當(dāng)程序運(yùn)行到instance.var = someVar,由于缺 setter方法會導(dǎo)致程序崩潰;或者當(dāng)運(yùn)行到 someVar = instance.var 時,由于缺 getter 方法同樣會導(dǎo)致崩潰。編譯時沒問題,運(yùn)行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定
18、ARC下,不顯式指定任何屬性關(guān)鍵字時,默認(rèn)的關(guān)鍵字都有哪些?
基本數(shù)據(jù):atomic,readwrite,assign
普通的 OC 對象:atomic,readwrite,strong
19、@synthesize合成實(shí)例變量的規(guī)則是什么?假如 property名為 foo,存在一個名為_foo的實(shí)例變量,那么還會自動合成新變量么?
先回答第二個問題:不會
@synthesize 合成成員變量的規(guī)則,有以下幾點(diǎn):
如果指定了成員變量的名稱,會生成一個指定的名稱的成員變量
如果這個成員已經(jīng)存在了就不再生成了
如果指定@synthesize foo;就會生成一個名稱為 foo 的成員變量,也就是說:會自動生成一個屬性同名的成員變量 @interfaceXMGPerson:NSObject
@property (nonatomic, assign) int age;
@end
@implementation XMGPerson
// 不加這語句默認(rèn)生成的成員變量名為_age
// 如果加上這一句就會生成一個跟屬性名同名的成員變量
@synthesize age;
@end
如果是 @synthesize foo = _foo; 就不會生成成員變量了
20、 在有了自動合成屬性實(shí)例變量之后 ,@synthesize還有哪些使用場景?
首先的搞清楚什么情況下不會 autosynthesis(自動合成)
同時重寫了 setter 和 getter 時
重寫了只讀屬性的 getter 時
使用了@dynamic 時
在 @protocol 中定義的所有屬性
在 category 中定義的所有屬性
重載的屬性,當(dāng)你在子類中重載了父類中的屬性,必須使用@synthesize 來手動合成 ivar
應(yīng)用場景
當(dāng)你同時重寫了 setter 和 getter 時,系統(tǒng)就不會生成 ivar)。這時候有兩種選擇
1.手動創(chuàng)建 ivar
2.使用@synthesize foo = _foo;,關(guān)聯(lián)@property 與 ivar
可以用來修改成員變量名,一般不建議這么做,建議使用系統(tǒng)自動生成的成員變量
21、怎么用 copy 關(guān)鍵字?
NSString、NSArray、NSDictionary 等等經(jīng)常使用 copy 關(guān)鍵字,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,為確保對象中的屬性值不會無意間變動,應(yīng)該在設(shè)置新屬性值時拷貝一份,保護(hù)其封裝性
block 也經(jīng)常使用 copy 關(guān)鍵字
block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內(nèi)部的block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).
在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的,但是建議寫上 copy,因?yàn)檫@樣顯示告知調(diào)用者“編譯器會自動對 block 進(jìn)行了 copy 操作”
22、用@property聲明的 NSString(或 NSArray,NSDictionary)經(jīng)常使用 copy關(guān)鍵字,為什么?如果改用 strong關(guān)鍵字,可能造成什么問題?
因?yàn)楦割愔羔樋梢灾赶蜃宇悓ο?使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.如果我們使用是 strong,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
復(fù)制詳解
淺復(fù)制(shallow copy):在淺復(fù)制操作時,對于被復(fù)制對象的每一層都是指針復(fù)制。
深復(fù)制(one-level-deepcopy):在深復(fù)制操作時,對于被復(fù)制對象,至少有一層是深復(fù)制。
完全復(fù)制(real-deepcopy):在完全復(fù)制操作時,對于被復(fù)制對象的每一層都是對象復(fù)制。
非集合類對象的 copy 與 mutableCopy [不可變對象 copy] // 淺復(fù)制
[不可變對象 mutableCopy] //深復(fù)制
[可變對象 copy] //深復(fù)制
[可變對象 mutableCopy] //深復(fù)制
集合類對象的 copy 與 mutableCopy [不可變對象 copy] // 淺復(fù)制
[不可變對象 mutableCopy] //單層深復(fù)制
[可變對象 copy] //單層深復(fù)制
[可變對象 mutableCopy] //單層深復(fù)制
這里需要注意的是集合對象的內(nèi)容復(fù)制僅限于對象本身,對象元素仍然是指針復(fù)制
23、這個寫法會出什么問題: @property(copy) NSMutableArray *array;
因?yàn)?copy 策略拷貝出來的是一個不可變對象,然而卻把它當(dāng)成可變對象使用,很容易造成程序奔潰。這里還有一個問題,該屬性使用了同步鎖,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序,這會帶來性能問題,通過聲明 nonatomic 可以節(jié)省這些,雖然很小但是不必要額外開銷,在 iOS開發(fā)中應(yīng)該使用 nonatomic替代 atomic
24、如何讓自定義類可以用 copy 修飾符?如何重寫帶 copy 關(guān)鍵字的 setter?
若想令自己所寫的對象具有拷貝功能,則需實(shí)現(xiàn) NSCopying 協(xié)議。如果自定義的對象分為可變版本與不可變版本 , 那么就要同時實(shí)現(xiàn) NSCopyiog 與NSMutableCopying 協(xié)議,不過一般沒什么必要,實(shí)現(xiàn) NSCopying 協(xié)議就夠了
// 實(shí)現(xiàn)不可變版本拷貝
- (id)copyWithZone:(NSZone *)zone;
// 實(shí)現(xiàn)可變版本拷貝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重寫帶 copy 關(guān)鍵字的 setter
- (void)setName:(NSString *)name
{
_name = [name copy];
}
25、+(void)load; +(void)initialize;有什么用處?
+(void)load;
當(dāng)類對象被引入項(xiàng)目時, runtime 會向每一個類對象發(fā)送 load 消息。
load 方法會在每一個類甚至分類被引入時僅調(diào)用一次,調(diào)用的順序:父類優(yōu)先于子類, 子類優(yōu)先于分類。
由于 load 方法會在類被 import 時調(diào)用一次,而這時往往是改變類的行為的最佳時機(jī),在這里可以使用例如 method swizlling 來修改原有的方法。
load 方法不會被類自動繼承。
+(void)initialize;
也是在第一次使用這個類的時候會調(diào)用這個方法,也就是說 initialize 也是懶加載
總結(jié):
在 Objective-C 中,runtime 會自動調(diào)用每個類的這兩個方法
1.+load 會在類初始加載時調(diào)用
2.+initialize 會在第一次調(diào)用類的類方法或?qū)嵗椒ㄖ氨徽{(diào)用
這兩個方法是可選的,且只有在實(shí)現(xiàn)了它們時才會被調(diào)用
兩者的共同點(diǎn):兩個方法都只會被調(diào)用一次
26、Foundation對象與 Core Foundation對象有什么區(qū)別
Foundation 框架是使用 OC 實(shí)現(xiàn)的,Core Foundation 是使用 C 實(shí)現(xiàn)的。
Foundation 對象 和 Core Foundation 對象間的轉(zhuǎn)換:俗稱橋接
ARC 環(huán)境橋接關(guān)鍵字:
// 可用于 Foundation對象 和 Core Foundation對象間的轉(zhuǎn)換
__bridge
// 用于Foundation對象 轉(zhuǎn)成 Core Foundation對象
__bridge_retained
// Core Foundation對象 轉(zhuǎn)成 Foundation對象
__bridge_transfer
Foundation 對象 轉(zhuǎn)成 Core Foundation 對象
使用__bridge橋接 。如果使用__bridge橋接,它僅僅是將 strOC 的地址給了 strC, 并沒有轉(zhuǎn)移對象的所有權(quán),也就是說, 如果使用__bridge 橋接, 那么如果 strOC 釋放了,strC 也不能用了。
注意:在 ARC 條件下,如果是使用__bridge 橋接,那么 strC 可以不用主動釋放, 因?yàn)锳RC 會自動管理 strOC 和 strC 。
NSString *strOC1 = [NSString stringWithFormat:@"abcdefg"];
CFStringRef strC1 = (__bridge CFStringRef)strOC1;
NSLog(@"%@ %@", strOC1, strC1);
使用__bridge_retained橋接
如果使用__bridge_retained 橋接,它會將對象的所有權(quán)轉(zhuǎn)移給 strC, 也就是說, 即便 strOC被釋放了, strC也可以使用。
注意:在 ARC 條件下,如果是使用__bridge_retained 橋接,那么 strC 必須自己手動釋放,因?yàn)闃蚪拥臅r候已經(jīng)將對象的所有權(quán)轉(zhuǎn)移給了 strC,而 C 語言的東西不是不歸ARC 管理的 。
NSString*strOC2=[NSStringstringWithFormat:@"abcdefg"];
// CFStringRef strC2 = (__bridge_retained
CFStringRef)strOC2;
CFStringRef strC2 = CFBridgingRetain(strOC2);// 這一句, 就
等同于上一句
CFRelease(strC2);
Core Foundation 對象 轉(zhuǎn)成 Foundation 對象
使用__bridge 橋接
如果使用__bridge 橋接,它僅僅是將 strC 的地址給了 strOC, 并沒有轉(zhuǎn)移對象的所有權(quán)。也就是說如果使用__bridge 橋接,那么如果 strC 釋放了,strOC 也不能用了。
CFStringRef strC3 =
CFStringCreateWithCString(CFAllocatorGetDefault(),
"12345678", kCFStringEncodingASCII);
NSString *strOC3 = (__bridge NSString *)strC3;
CFRelease(strC3);
使用__bridge_transfer 橋接
如果使用__bridge_transfer 橋接,它會將對象的所有權(quán)轉(zhuǎn)移給 strOC, 也就是說, 即便 strC被釋放了, strOC也可以使用 。
如果使用__bridge_transfer 橋接, 他會自動釋放 strC, 也就是以后我們不用手動釋放 strC。
CFStringRef strC4 =
CFStringCreateWithCString(CFAllocatorGetDefault(),
"12345678", kCFStringEncodingASCII);
// NSString *strOC = (__bridge_transfer NSString
*)strC;
NSString *strOC4 = CFBridgingRelease(strC4); // 這一句, 就
等同于上一句
MRC 環(huán)境:直接強(qiáng)轉(zhuǎn)
-(void)bridgeInMRC {
// 將 Foundation對象轉(zhuǎn)換為 CoreFoundation對象,直接強(qiáng)制類
型轉(zhuǎn)換即可
NSString *strOC1 = [NSString
stringWithFormat:@"xxxxxx"];
CFStringRef strC1 = (CFStringRef)strOC1;
NSLog(@"%@ %@", strOC1, strC1);
[strOC1 release];
CFRelease(strC1);
// 將 CoreFoundation對象轉(zhuǎn)換為 Foundation對象,直接強(qiáng)制類
型轉(zhuǎn)換即可
CFStringRef strC2 =
CFStringCreateWithCString(CFAllocatorGetDefault(),
"12345678", kCFStringEncodingASCII);
NSString *strOC2 = (NSString *)strC2;
NSLog(@"%@ %@", strOC2, strC2);
[strOC2 release];
CFRelease(strC2);
}
27、addObserver:forKeyPath:options:context:各個參數(shù)的作用分別是什么,observer中需要實(shí)現(xiàn)哪個方法才能獲得 KVO回調(diào)?
/**
1. self.person:要監(jiān)聽的對象
2. 參數(shù)說明
1> 觀察者,負(fù)責(zé)處理監(jiān)聽事件的對象
2> 要監(jiān)聽的屬性
3> 觀察的選項(xiàng)(觀察新、舊值,也可以都觀察)
4> 上下文,用于傳遞數(shù)據(jù),可以利用上下文區(qū)分不同的監(jiān)聽
*/
[self.person addObserver:self forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:@"Person Name"];
/**
* 當(dāng)監(jiān)控的某個屬性的值改變了就會調(diào)用
*
* @param keyPath 監(jiān)聽的屬性名
* @param object 屬性所屬的對象
* @param change 屬性的修改情況(屬性原來的值、屬性最新的值)
* @param context 傳遞的上下文數(shù)據(jù),與監(jiān)聽的時候傳遞的一致,可以利用上下
文區(qū)分不同的監(jiān)聽
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@對象的%@屬性改變了:%@", object, keyPath, change);
}
28、KVO內(nèi)部實(shí)現(xiàn)原理?
KVO 是基于 runtime 機(jī)制實(shí)現(xiàn)的
當(dāng)某個類的屬性對象第一次被觀察時,系統(tǒng)就會在運(yùn)行期動態(tài)地創(chuàng)建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。派生類在被重寫的 setter 方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制
如果原類為 Person,那么生成的派生類名為NSKVONotifying_Person每個類對象中都有一個 isa 指針指向當(dāng)前類,當(dāng)一個類對象的第一次被觀察,那么系統(tǒng)會偷偷將 isa 指針指向動態(tài)生成的派生類,從而在給被監(jiān)控屬性賦值時執(zhí)行的是派生類的 setter 方法鍵值觀察通知依賴于NSObject的兩個方法 : willChangeValueForKey: 和 didChangevlueForKey:;在一個被觀察屬性發(fā)生改變之前, willChangeValueForKey:一定會被調(diào)用,這就 會記錄舊的值。而當(dāng)改變發(fā)生后,didChangeValueForKey: 會被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用。
補(bǔ)充:KVO 的這套實(shí)現(xiàn)機(jī)制中蘋果還偷偷重寫了 class 方法,讓我們誤認(rèn)為還是使用的當(dāng)前類 ,從而達(dá)到隱藏生成的派生類。
29、如何手動觸發(fā)一個 value的 KVO
自動觸發(fā)的場景:在注冊 KVO 之前設(shè)置一個初始值,注冊之后,設(shè)置一個不一樣的值,就可以觸發(fā)了。
想知道如何手動觸發(fā),必須知道自動觸發(fā) KVO 的原理,見上面的描述手動觸發(fā)演示。
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad
{
[super viewDidLoad];
// “手動觸發(fā) self.now的 KVO”,必寫。
[self willChangeValueForKey:@"now"];
// “手動觸發(fā) self.now的 KVO”,必寫。
[self didChangeValueForKey:@"now"];
}
30、若一個類有實(shí)例變量 NSString*_foo,調(diào)用setValue:forKey:時,是以 foo還是_foo作為key?
都可以
31、KVC的 keyPath中的集合運(yùn)算符如何使用?
必須用在集合對象上或普通對象的集合屬性上
簡單集合運(yùn)算符有@avg, @count , @max , @min ,@sum
格式 @"@sum.age" 或 @"集合屬性.@max.age"???
32、KVC和 KVO的 keyPath一定是屬性么?
可以是成員變量
33、如何關(guān)閉默認(rèn)的 KVO的默認(rèn)實(shí)現(xiàn),并進(jìn)入自定義的 KVO實(shí)現(xiàn)?
34、apple用什么方式實(shí)現(xiàn)對一個對象的 KVO?
此題就是問 KVO 的實(shí)現(xiàn)原理,(28題)