版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
iOS程序員面試分類模擬8簡答題1.
什么是消息推送?和NotificatiOil有什么區(qū)別?正確答案:消息推送指在App關(guān)閉時(不在前臺運行時),仍然向用戶發(fā)送App的內(nèi)部消息。消息推送通知和O(江南博哥)bjective-C中的Notification通知機制不同,推送的消息是給用戶看的,也就是可見的,而通知機制是Objective-C語言中對象間通信的一種機制,基于觀察者模式,目的是觸發(fā)內(nèi)部事件,減小類之間的耦合度,對用戶是不可見的。
推送消息的可見形式主要有以下幾種:
1)鎖屏界面的橫幅推送消息。
2)頂部通知欄的橫幅推送消息。
3)應用圖標上的代表消息數(shù)量的紅色數(shù)字。
4)菜單頁面彈出框提示。
5)播放聲音提示。
下圖所示是iPhone中QQ推送的設(shè)置界面。
QQ推送的設(shè)置界面
下圖所示為iPhone中消息推送的形式。
iPhone中消息推送的形式
iOS開發(fā)中有兩種類型的消息推送:本地消息推送(LocalNotification)和遠程消息推送(RemoteNotification)。
1)本地消息推送:本地消息推送很簡單,不需要聯(lián)網(wǎng),不需要服務(wù)器,由客戶端應用直接發(fā)出推送消息,一般通過定時器在指定的時間進行消息推送。
2)遠程消息推送:遠程消息推送過程略為復雜,需要客戶端從蘋果公司的APNS(ApplePushNotificationSerwices)服務(wù)器注冊獲得當前用戶的設(shè)備令牌并發(fā)送給應用的服務(wù)器,然后應用的服務(wù)器才可以通過APNS服務(wù)器間接地向客戶端發(fā)送推送消息,期間難免會有延遲。
遠程消息推送的具體流程如圖所示,開發(fā)中要和服務(wù)器合作共同完成。
遠程消息推送流程圖
①App客戶端向APNS服務(wù)器發(fā)送設(shè)備的UDID和BundleIdentifier。
②APNS服務(wù)器對傳過來的信息加密生成一個deviceToken,并返回給客戶端。
③客戶端將當前用戶的deviceToken發(fā)送給自己應用的服務(wù)器。
④自己應用的服務(wù)器將得到的deviceToken保存,需要的時候利用deviceToken向APNS服務(wù)器發(fā)送推送消息。
⑤APNS服務(wù)器接收到自己應用的服務(wù)器的推送消息時,驗證傳過來的deviceToken,如果一致,那么將消息推送到客戶端。
2.
什么是Notification?什么時候用Delegate或Notification?正確答案:Notification(通知)是Cocoa框架中基于觀察者模式實現(xiàn)的用于一對多傳播消息的一種機制。項目中的對象將它們自己或者其他對象添加到通知的觀察者列表里(這個過程又叫通知注冊),其中項目中的所有通知都有一個唯一的字符串標識作為通知名唯一確定每個通知,通知源也就是被觀察者可以創(chuàng)建通知對象并發(fā)送到通知中心,通知中心找出所有注冊該通知的對象(觀察者),并將從被觀察者那里收到通知以消息的方式發(fā)送給所有的觀察者們。被觀察者發(fā)送通知是一個同步過程,即發(fā)送者在通知中心成功將該發(fā)送者之前的消息發(fā)送給所有觀察者之前不可以再次發(fā)送通知。另外,通知觸發(fā)的代理方法都必須符合某個單一參數(shù)簽名約定,代理方法的參數(shù)是一個通知對象,參數(shù)里包含著通知名、被觀察者和一個包含其他額外信息的字典。
Delegate和Notification的主要區(qū)別在于前者是一對一的消息傳遞,而后者是一對多的,可以根據(jù)這個特點在使用中進行選擇。另外在代理模式中,reciever(接收者)可以返回值給sender(發(fā)送者),實現(xiàn)一種回調(diào),而觀察者模式中觀察者不可以返回值給被觀察者,因此在需要實現(xiàn)回調(diào)時只能選擇代理模式。
3.
NSNotification是同步還是異步?正確答案:NSNotification默認在主線程中通知是同步的,當通知產(chǎn)生時,通知中心會一直等待所有的觀察者都收到并且處理通知結(jié)束,然后才會返回到發(fā)送通知的地方繼續(xù)執(zhí)行后面的代碼,但可以將通知的發(fā)送或者將通知的處理方法放到子線程中從而避免通知阻塞。其中,通知的發(fā)送可以添加到NSNotificationQueue異步通知緩沖隊列中,也不會導致通知阻塞。NSNotificationQueue是一個通知緩沖隊列,通常以FIFO(先進先出)的規(guī)則維護通知隊列的發(fā)送。向通知隊列添加通知有3種枚舉類型:NSPostASAP、NSPostWhenIdle和NSPostNow,分別表示盡快發(fā)送、空閑時發(fā)送和現(xiàn)在立刻發(fā)送,可以根據(jù)通知的緊急程度進行選擇。
下面示例驗證默認通知是同步的。
/*自定義消息的名稱*/
#defineMYNotificationTestName@"NSNotificationTestName"
/*1.注冊通知的觀察者*/
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(process)name:MYNotificationTestNameobject:nil];
/*2.發(fā)出通知給觀察者*/
NSLog(@"即將發(fā)出通知!");
[[NSNotificationCenterdefaultCenter]postNotificationName:MYNotificationTestNameobject:nil];
NSLog(@"發(fā)出通知處的下一條代碼!");
/*3.處理收到的通知*/
-(void)process{
sleep(10);//假設(shè)處理需要10s
NSLog(@"通知處理結(jié)束!");
}
程序的打印結(jié)果為:
2017-01-2122:21:30.501SingleView[4579:146073]即將發(fā)出通知!
2017-01-2122:21:40.572SingleView[4579:146073]通知處理結(jié)束!
2017-01-2122:21:40.572SingleView[4579:t46073]發(fā)出通知處的下一條代碼!
打印“即將發(fā)出通知”后,等了10s之后才打印出“通知處理結(jié)束”,然后打印出@“發(fā)出通知處的下一條代碼!”?!鞍l(fā)出通知處的下一條代碼!”是等到通知處理結(jié)束才打印出來的,說明通知是同步的。
可以通過將通知的發(fā)送語句或者通知的處理語句放到子線程實現(xiàn)通知的異步。
將通知的發(fā)送語句放到子線程:
NSLog(@"即將發(fā)出通知!");
dispatch_async(dispatch_get_global_queue(0,0),^{
[[NSNotificationCenterdefaultCenter]
postNotificationName:MYNotificationTestNameobject:nil];
});
NSLog(@"發(fā)出通知處的下一條代碼!");
或者:
NSLog(@"即將發(fā)出通知!");
/*將通知放到通知異步緩沖隊列*/
NSNotification*notification=[NSNotificationnotificationWithName:MYNotificationTestNameobject:nil];
[[NSNotificationQueuedefaultQueue]enqueueNotification:notificationpostingStyle:NSPostASAP];
NSLog(@"發(fā)出通知處的下一條代碼!");
將通知的處理放到子線程:
/*處理收到的通知*/
-(void)process{
dispatch_async(dispatch_get_global_queue(0,0),^{
sleep(10);//假設(shè)處理需要10s
NSLog(@"通知處理結(jié)束!");
});
}
執(zhí)行結(jié)果變?yōu)椋?/p>
2017-01-2122:31:09.259SingleView[4711:151180]即將發(fā)出通知!
2017-01-2122:31:09.260SingleView[4711:151180]發(fā)出通知處的下一條代碼!
2017-01-2122:3l:19.290SingleView[4711:151252]通知處理結(jié)束!
4.
什么是鍵值編碼KVC?鍵路徑是什么?什么是鍵值觀察KVC?正確答案:1.鍵值編碼KVC
鍵值編碼(KVC)是一種在NSKeyValueCoding非正式協(xié)議下使用字符串標志間接訪問對象屬性的一種機制,也就是訪問對象變量的一種特殊的捷徑。如果一個對象符合鍵值編碼的約定,那么它的屬性就可以通過一個準確的、唯一的字符串(鍵路徑字符串)參數(shù)進行訪問,類似于將所有對象看作Dictionary,鍵路徑為key(實際為keypath),屬性值即value,通過鍵路徑訪問屬性值。鍵值編碼的間接訪問方式其實是傳統(tǒng)實例變量的存取方法訪問的一種替代,也就是另外一種可以訪問對象變量的方法。其中,注意鍵值編碼可以暴力訪問對象的任何變量,無論是否是pirvate私有類型的變量。
開發(fā)者通常是通過存取方法來訪問對象的屬性的,getter方法返回屬性的值,setter方法設(shè)置屬性的值。對于實例對象,可以直接通過存取方法或者變量名來訪問對象的屬性,但是隨著屬性數(shù)量的增加和對象變量的嵌套深度增大,訪問代碼會隨之增多。相比之下,通過鍵值編碼就可以簡潔而穩(wěn)定地對所有屬性進行訪問。
鍵值編碼是Cocoa框架中很基礎(chǔ)的一個概念,像KVO、Cocoa綁定、CoreData等都是基于KVC的。
2.鍵路徑
鍵路徑就是鍵值編碼中某個屬性的key,一個由連續(xù)鍵名組成的字符串,鍵名即屬性名,鍵名之間用點隔開,用于指定一個連接在一起的對象性質(zhì)序列。鍵路徑使開發(fā)者可以獨立于模型實現(xiàn)的方式指定相關(guān)對象的性質(zhì)。通過鍵路徑,可以指定對象圖中的一個任意深度的路徑,使其指向相關(guān)對象的某個特定的屬性。
3.鍵值觀察KVO
鍵值觀察(KVO),是基于鍵值編碼實現(xiàn)的一種觀察者機制,提供了觀察某一屬性變化的監(jiān)聽方法,用來簡化代碼,優(yōu)化邏輯和組織。
這里提供一個最基本的KVO和KVC的使用示例。假設(shè)有一個專業(yè)類Major,Major有一個專業(yè)名(majorName)私有屬性;一個學生類Student,Student有一個姓名(name)私有屬性,同時還有一個專業(yè)(Major)對象變量,這樣就出現(xiàn)了簡單的Major和Student的對象嵌套。Major和Student都繼承自NSObject,都符合鍵值編碼約定,可以定義一個Student變量對其進行鍵值編碼和鍵值觀察。示例代碼如下:
/*1.專業(yè)類模型:Major.h*/
@interfaceMajor:NSObject{
@private
NSString*majorName;//私有實例變量專業(yè)名稱
}
@end
/*2.學生類模型:Student.h*/
@interfaceStudent:NSObject{
@private
NSString*name;//私有實例變量姓名
}
@property(nonatomic,strong)Major*major;//學生專業(yè)
/*3.kvc和kvo之間基本使用方法*/
#import"Student.h"
#import"Major.h"
@interfaceViewController()
@property(nonatomic,strong)Student*student;
@end
@implementationViewController
(void)viewDidLoad{
[superviewDidLoad];
/*初始化學生Student對象*/
_student=[[Studentalloc]init];
/*初始化學生的專業(yè)Major對象*/
_student.major=[[Majoralloc]init];
/*1.set:通過KVC設(shè)置Student對象的值(可以強行訪問private變量)*/
[_studentsetValue:@"Sam"forKey:@"name"];
[_studentsetValue:@"ComputerScience"forKeyPath:@"major.majorName"];
/*2.get:通過KVC讀取Student對象的值(可以強行訪問private變量)*/
NSLog(@"%@",[_studentvalueForKey:@"name"]);
NSLog(@"%@",[_studentvalueForKeyPath:@"major.majorName"]);
/*
**3.kvo:添加當前控制器為鍵路徑major.majorName的一個觀察者,
**如果major.majorNalne的值改變,那么會通知當前控制器從而
**自動調(diào)用下面的observeValueForKeyPath方法,這里傳遞舊值和新值
*/
[_studentaddObserver:selfforKeyPath:@"major.majorName"options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNewcontext:nil];
/*3s后改變major.majorName的值*/
dispatch_after(dispatch_time(DISPATCH_TIMENOW,(int64_t)(3*NSEC_PER_SEC)),dispatch_get_main_queue(),^{
[_studentsetValue:@"SoftwareEngineer"forKeyPath:@"major.majorName"];
});
}
/*監(jiān)聽keyPath值的變化*/
-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary<NSKeyValueChangeKey,id>*)changecontext:(void*)context{
if([keyPathisEqual:@"major.majorName"]){
/*獲取變化前后的值并打印*/
NSString*oldValue=[changeobjectForKey:NSKeyVatueChangeOldKey];
NSString*newValue=[changeobjectForKey:NSKeyValueChangeNewKey];
NSLog(@"major.majorNamevaluechanged:oldValue:%@newValue:%@",oldValue,newValue);
}
else{
[superobserveValueForKeyPath:keyPathofObject:objectchange:changecontext:context];
}
}
/*移除觀察者釋放資源,防止資源泄漏*/
-(void)dealloc{
[_StudentremoveObserver:selfforKeyPath:@"major.majorName"];
}
@end
可以看到major.majorName就是一個由兩個鍵連接起來的基本鍵路徑,相當于用它可以直接訪問屬性的屬性;使用addObserver方法將當前控制器注冊為Student對象的觀察者,當Student中鍵路徑major.majorName下的值發(fā)生改變時會通知當前控制器,觸發(fā)observeValueForKeyPath監(jiān)聽方法。
5.
KVC的應用場景有哪些?正確答案:鍵值編碼(Key-valueCoding,KVC),在iOS開發(fā)中,允許開發(fā)者通過key名間接訪問對象的屬性,或者給對象的屬性賦值,而不需要調(diào)用屬性的存取方法。這樣就可以在運行時動態(tài)地訪問和修改對象的屬性值。
在Objective-C中,NSObject類及其子類默認都實現(xiàn)了NSKeyValueCoding協(xié)議,這個協(xié)議定義了所有實現(xiàn)KVC的方法。下面列舉了KVC中最為重要的4個方法。
/*直接通過Key來取值*/
-(nullableid)valueForKey:(NSString*)key;
/*通過Key來設(shè)值*/
-(void)setValue:(nullableid)valueforKey:(NSString*)key;
/*通過KeyPath來取值*/
-(nullableid)valueForKeyPath:(NSString*)keyPath;
/*通過KeyPath來設(shè)值-/
-(void)setValue:(nullableid)valueforKeyPath:(NSString*)keyPath;
開發(fā)者可以通過這些方法,直接使用NSString類型的key值,獲取或者設(shè)置對象中對應value的值。
KVC在iOS開發(fā)中至關(guān)重要,這種基于運行時的編程方式極大地提高了代碼的靈活性,簡化了代碼邏輯,甚至實現(xiàn)很多難以想象的功能。KVC也是許多iOS開發(fā)高級技巧的基礎(chǔ)。一般可以在以下場景中使用KVC。
1.動態(tài)地取值和設(shè)值
這應該是KVC最常用到的地方了,也是KVC的基本用途。相信每一個iOS開發(fā)者都應該熟練掌握。
2.利用KVC來訪問、修改對象的私有變量和屬性
對于類中的私有屬性,無法通過普通的設(shè)值和取值方法直接訪問,但是KVC是可以的。例如,在UITextField中隱藏著一個padding屬性,這個屬性能夠為UITextField類型對象設(shè)置內(nèi)邊距。示例代碼如下:
UITextField*tf=[UITextFieldalloc]init];
UIEdgeInsetsinset=UIEdgeInsetsMake(0,10,0,0);
[tfsetValue:[NSValuevalueWithUIEdgeInsets:inset]forKeyPath:@"padding"];
3.利用KVC進行Model和字典之間的轉(zhuǎn)換
使用KVC中提供的setValuesForKeysWithDictionary:,可以將字典映射到一個對象,不需要一一地為對象賦值而直接從字典中初始化即可。
4.利用KVC實現(xiàn)高階消息傳遞
當對容器類(如NSArray類)使用KVC時,valueForKey:將會被傳遞給容器中的每一個對象,而不是對容器本身進行操作。相應的結(jié)果會被添加進返回的容器中,這樣可以很方便地操作集合來返回另一個集合。示例代碼如下:
NSArray*test=@[@"jack",@"sam",@"tom"];
NSArray*result=[testvalueForKey:@"capitalizedString"];
[resultenumerateObjectsUsingBlock:^(id_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop){
NSLog(@"idx=%zdobj=%@",idx,obj);
}];
程序的輸出結(jié)果如下:
2016-10-3020:43:27.51601[40402:1717920]idx=0obj=Jack
2016-10-3020:43:27.51701[40402:1717920]idx=1obj=Sam
2016-10-3020:43:27.51801[40402:1717920]idx=2obj=Tom
方法capitalizedString被傳遞到數(shù)組test中的每一項,每個數(shù)組成員都會執(zhí)行capitalizedString方法并返回一個包含新結(jié)果的NSArray,即所有的字符串的第一個字符都轉(zhuǎn)成了大寫。這里需要注意的是,如果要執(zhí)行多個方法,那么可以使用valueForKeyPath:方法。
6.
如何運用KVO進行鍵值觀察?正確答案:鍵值觀察者模式(Key-valueObserver,KVO)是觀察者模式在Objective-C中的一種運用方式。其基本思想是:一個被觀察目標管理所有依賴于它的觀察者對象,并在它自身的屬性發(fā)生改變時主動通知所有觀察者對象,觀察者對象可以對改變做出相應的處理。通過這種機制,KVO能夠較完美地將目標對象與觀察者對象解耦。
利用KVO實現(xiàn)鍵值觀察主要通過以下幾個步驟:
1.注冊成為觀察者
如果需要觀察某個對象的屬性值變化,那么要通過調(diào)用NSObject的NSKeyValueObserverRegistration類別的如下方法來注冊成為被觀察對象的觀察者。
-(void)addObserver:(NSObject*)observerforKeyPath:(NSString*)keyPathoptions:(NSKeyValueObservingOptions)optionscontext:(nullablevoid*)context;
參數(shù)observer是觀察者對象,通常使用控制器對象作為觀察者。參數(shù)keyPath是被觀察者對象中需要被觀察的屬性名稱。參數(shù)options是NSKeyValueObservingOptions類型的枚舉,可以根據(jù)實際觀察需要進行配置。參數(shù)context是上下文,可以在這里為KVO的回調(diào)方法傳遞參數(shù)。示例代碼如下:
/*創(chuàng)建一個需要被觀察對象的類*/
#import<Foundation/Foundation.h>
@interfacePerson:NSObject
@property(nonatomic,strong)NSString*name;
@property(nonatomic,assign)NSIntegerage;
@end
#import"Person.h"
@implementationPerson
@end
/*其次在控制器中實例化一個被觀察者對象并將控制器對象注冊成為觀察者*/
/*options參數(shù)設(shè)置為NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld,表示同時觀察新值和舊值*/
aperson=[[Personalloc]init];
[apersonaddObserver:selfforKeyPath:@"age"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:nil];
2.設(shè)置需要被觀察對象的屬性
注冊好觀察者之后,當設(shè)置被觀察者對象的屬性時,屬性值得變更情況就會被通知給觀察者對象。此處需要注意的是,只有嚴格遵循KVC方式來設(shè)置屬性值,觀察者對象才會獲取通知,也就是說遵循使用屬性的setter方法或key-path來設(shè)置。如果沒有通過setter方法或者KVC,而是直接修改觀察者對象屬性對應的成員變量,那么就不會觸發(fā)KVO機制,當然也就不會產(chǎn)生回調(diào)。這里在視圖的單擊方法中設(shè)置aperson的屬性值。
-(void)touchesBegan:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event{
aperson.age=20;
[apersonsetValue:@"sam"forKey:@"name"];
}
3.在回調(diào)方法中處理變更通知
所有的觀察者需要實現(xiàn)NSKeyValueObserving類別中的如下回調(diào)方法來處理收到的變更通知。
-(void)observeValueForKeyPath:(nullableNSString*)keyPathofObject:(nullableid)objectchange:(nullableNSDictionary<NSKeyValueChangeKey,id>*)changecontext:(nullablevoid*)context;
其中,參數(shù)keyPath是需要觀察的屬性名稱,和注冊成為觀察者時傳入的keyPath參數(shù)一致。參數(shù)object代表被觀察對象。參數(shù)change是一個字典對象,被觀察屬性變化前后的值都存儲在這個對象中,具體是哪些信息取決于注冊時NSKeyValueObservingOptions類型的options參數(shù)。參數(shù)context對應注冊時傳入的context參數(shù)。接下來在控制器中實現(xiàn)這個回調(diào)方法。
-(void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id>*)changecontext:(void*)context{
if([keyPathisEqualToString:@"age"]){
NSLog(@"oldAge=%@,newAge=%@",change[NSKeyValueChangeOldKey],change[NSKeyValueChangeNewKey]);
}elseif([keyPathisEqualToString:@"name"]){
NSLog(@"oldName=%@,newName=%@",change[NSKeyValueChangeOldKey],change[NSKeyValueChangeNewKey]);
}
}
程序的輸出結(jié)果如下:
2016-10-3118:15:15.82601[53433:2090647]oldAge=10,newAge=20
2016-10-3118:15:15.82601[53433:2090647]oldName=litterSam,newName=Sam
4.移除觀察者
需要強調(diào)的是,注冊成為觀察者之后需要在適當?shù)臅r候移除觀察者對象,否則會導致內(nèi)存溢出。解除方法也是定義在NSKeyValueObserverRegistration類別中:
-(void)removeObserver:(NSObject*)observerforKeyPath:(NSString*)keyPath;
在這個例子中,需要在控制銷毀之前解除觀察值,代碼如下:
-(void)dealloc{
[apersonremoveObserver:selfforKeyPath:@"age"];
[apersonremoveObserver:selfforKeyPath:@"name"];
}
7.
如何使用KVO設(shè)置鍵值觀察依賴鍵?正確答案:很多情況下對象的屬性之間是相互關(guān)聯(lián)的,對象的一個屬性值依賴于另一個對象的一個或多個屬性,如果這些屬性中的任意一個值發(fā)生改變,那么被依賴屬性的值也會相應發(fā)生改變。如果要觀察被依賴屬性值的變更,那么就需要使用KVO中的依賴鍵。實現(xiàn)KVO中的依賴鍵最重要的部分是設(shè)置依賴關(guān)系。示例代碼如下:
/*新建一個Student類,其中的preson屬性是Person類的實例*/
#import<Foundation/Foundation.h>
#import"Person.h"
@interfaceStudent:NSObject
@property(nonatomic,strong)NSString*infomation;
@property(nonatomic,strong)Person*preson;
@end
#import"Student.h"
@implementationStudent
@end
此時需要觀察Student類實例中infomation屬性值的變化,而infomation屬性的值依賴于preson屬性的值,那么如何確立這種依賴關(guān)系呢?開發(fā)者要手動實現(xiàn)infomation的setter和getter方法,還要實現(xiàn)keyPathsForValuesAffectingInfomation或者keyPathsForValuesAffectingValueForKey:方法,這兩個方法的作用都是用來告訴系統(tǒng)infomation屬性依賴于其他哪些屬性,而且這兩個方法都返回包含著依賴key-path的集合。在例子中,infomation屬性依賴于person中的age和name屬性,當person中age或者name屬性發(fā)生改變時,infomation的觀察者都會得到通知。在Student.m文件中添加如下代碼:
-(NSString*)infomation{
return[NSStringstringWithFormat:@"student_name=%@,student_age=%zd",,self.preson.age];
}
-(void)setInfomation:(NSString*)infomation{
NSArray*array=[infomationcomponentsSeparatedByString:@"#"];
[self.presonsetName:[arrayobjectAtIndex:0]];
[self.presonsetAge:[[arrayobjectAtIndex:1]integerValue]];
}
+(NSSet*)keyPathsForValuesAffectingInfomation{
NSSet*set=[NSSetsetWithObjects:@"person.age",@"",nil];
returnset;
}
現(xiàn)在已經(jīng)建立了依賴關(guān)系,還需要在控制器中添加觀察者和實現(xiàn)KVO的回調(diào)方法。
-(void)viewDidLoad{
astudent=[[Studentalloc]init];
astudent.person=[[Personalloc]init];
[astudentaddObserver:selfforKeyPath:@"infomation"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:nil];
}
-(void)touchesBegan:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event{
astudent.person.age=20;
=@"jack";
}
-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary<NSKeyValueChangeKey,id>*)changecontext:(void*)context{
if([keyPathisEqualToString:@"infornation"]){
NSLog(@"old_infomation:%@new_infomation:%@",change[NSKeyValueChangeOldKey],change[NSKeyValueChangeNewKey]);
}
}
程序的輸出結(jié)果如下:
2016-10-3119:01:19.34601[53615:2109010]old_infomation:student_name=(null),student_age=0new_infomation:student_name=(null),student_age=20
2016-10-3119:01:19.34601[53615:2109010]old_infomation:student_name=(null),student_age=20new_infomation:student_name=jack,student_age=20
當分別為person屬性設(shè)置name和age時,infomation的值都會發(fā)生變化,所以會打印兩次infomation的信息。當然開發(fā)者仍然需要在恰當?shù)臅r候解除觀察者,代碼如下:
-(void)dealloc{
[astudentremoveObserver:selfforKeyPath:@"infomation"];
}
8.
KVO的背后原理是什么?正確答案:通過前面的問題可以看出KVO的強大,開發(fā)者只要在需要的時候注冊成為觀察者和實現(xiàn)KVO提供的回調(diào)方法,就可以獲得鍵值觀察的能力,這是很多iOS高級開發(fā)技巧的基礎(chǔ),但是系統(tǒng)在中間環(huán)節(jié)幫開發(fā)者做了哪些事情呢?答案就是Objective-C中強大的運行時(runtime)機制。
當某個類的對象第一次被觀察時,系統(tǒng)就會在運行時動態(tài)地創(chuàng)建對應于該類的一個派生類,在這個派生類中系統(tǒng)會重寫父類中被觀察屬性的setter方法。在重寫的setter方法中實現(xiàn)了真正的通知機制,類似下面手動實現(xiàn)鍵值觀察的方式。
-(void)setAge:(NSInteger)age{
[selfwillChangeValueForKey;@"age"];
_age=age;
[selfdidChangeValueForKey:@"age"];
}
+(BOOL)automaticallyNotifiesObserversForKey:(NSString*)key{
if([keyisEqualToString:@"age"]){
returnNO;
}
return[superautomaticallyNotifiesObserversForKey:key];
}
當調(diào)用age屬性的stter方法時,在屬性值改變的前后會分別調(diào)用willChangeValueForKey:方法和didChangeValueForKey:方法,這兩個方法分別用于通知系統(tǒng)該屬性值即將發(fā)生變化和已經(jīng)發(fā)生變化。實現(xiàn)了automaticallyNotifiesObserversForKey:方法后,在這個方法中取消了屬性變更發(fā)送通知的操作。要通過KVO來實現(xiàn)觀察者模式,必須遵循KVO的屬性設(shè)置方式(KVC或者setter方法)來變更屬性值,如果僅是直接修改屬性對應的成員變量,那么是無法實現(xiàn)KVO的。
當系統(tǒng)在運行時創(chuàng)建派生類的同時,派生類還重寫了class方法來“欺騙”開發(fā)者,讓開發(fā)者以為正在調(diào)用的類還是原始類。然后系統(tǒng)會將這個對象的isa指針指向新創(chuàng)建的派生類,因此這個對象就成為該派生類的對象了,因而當開發(fā)者調(diào)用屬性的setter方法時,實際上是調(diào)用了重寫之后的setter方法,從而實現(xiàn)了鍵值通知機制。此外,派生類還重寫了dealloc方法來釋放內(nèi)存資源。
9.
setValHe:forKey:方法的底層實現(xiàn)是什么?正確答案:當一個對象發(fā)送setValue:forKey:消息時,方法內(nèi)部會做以下操作:
1)查對象的類中是否存在與key相對應的訪問器方法(即-set<key>),如果存在,那么就會直接調(diào)用訪問器方法。
2)如果訪問器方法不存在,那么就會繼續(xù)查找與key的名稱相同并且?guī)А癬”前綴的成員變量(即_key)。如果存在這樣的成員變量并且類型是一個對象指針類型,那么就會先released成員變量的舊值,然后直接為對象的這個成員變量賦新值。
3)如果_key不存在,那么就會繼續(xù)查找與key的名稱相同的屬性。如果有這樣的屬性,那么就會直接為對象的這個屬性賦值。
4)如果訪問器方法、成員變量和屬性都沒有找到,那么就會調(diào)用setValue:forUndefinedKey:方法,該方法的默認實現(xiàn)是拋出一個NSUndefinedKeyException類型的異常,但是可以根據(jù)需要重寫setValue:forUndefinedKey:方法。
示例代碼如下:
/*定義一個Person類*/
#import"Person.h"
@interfacePerson(){
NSString*_name;
int_age;
}
@property(nonatomic,assign)intage;
@property(nonatomic,copy)NSString*address;
@end
@implementationPerson
-(void)setName:(NSString*)name
{
NSLog(@"%s",__func__);
_name=name;
}
-(void)log{
NSLog(@"age=%iaddress=%@",__age,self.address);
}
/*編輯如下測試代碼:*/
Person*p=[Personnew];
[psetValue:@"小強"forKey:@"name"];
[psetValue:@25forKey:@"age"];
[psetValue:@"合肥市"forKey:@"address"];
[plog];
程序的輸出結(jié)果為:
2016-10-1017:47:59.886mianshibaodian[4408:519039]-[PersonsetName:]
2016-10-1017:47:59.887mianshibaodian[4408:519039]age=25
address=合肥市
2016-10-1018:07:15.081mianshibaodian[4471:531349]***Terminatingappduetouncaughtexception'NSUnknownKeyException',reason:'[<Person0x60800003dde0>setValue:forUndefinedKey:]:thisclassisnotkeyvaluecoding-compliantforthekeyundefineAddress.'
輸出結(jié)果驗證了setValue:forKey:方法會調(diào)用訪問器方法(setName:),對name成員變量進行賦值。第二行輸出驗證了當不存在訪問器方法時,setValue:forKey:方法會逐步從對象的類中尋找?guī)А癬”的成員變量和屬性進行賦值,這里特別需要特別強調(diào)一點setValue:傳入的參數(shù)類型必須是對象類型或者NSValue類型,否則會產(chǎn)生錯誤。最后一行驗證了當訪問器方法、成員變量和屬性都沒有找到時,就會拋出一個異常,當然可以在Person類中重寫setValue:forUndefinedKey:方法,自己來處理這個異常,這里不做過多的解釋。
10.
NSMutableDictionary中setValue和setObject有什么區(qū)別?正確答案:需要了解蘋果公司API中關(guān)于NSMutableDictionary中setValue:forKey:方法和setObject:forKey:方法的定義。其中,對應setValue:forKey:方法的API是:
@interfaceNSMutableDictionary<KeyType,ObjectType>(NSKeyValueCoding)
/*Send
-setObject:forKey:tothereceiver,unlessthevalueisnil,inwhichcasesend-removeObjectForKey:*/
-(void)setValue:(nullableObjectType)valueforKey:(NSString*)key;
@end
官方的注釋說得很清楚,當發(fā)送setValue:forKey:消息給一個NSMutableDictionary類型的對象時,一般情況下仍然是調(diào)用了setObject:forKey方法,除非,當參數(shù)value的值是nil的時候,就會轉(zhuǎn)而調(diào)用removeObjectForKey:清除這個鍵值對。
對應setobject:forKey:方法的API是:
-(void)setObject:(id)anObjectforKey:(id<NSCopying>)aKey;
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025高考數(shù)學考點剖析精創(chuàng)專題卷七-空間向量與立體幾何【含答案】
- 糖尿病視網(wǎng)膜病變病例討論(共30張課件)
- 江西省贛州市興國縣高興鎮(zhèn)高興小學-主題班會-網(wǎng)絡(luò)安全教育【課件】
- 二零二五年短視頻平臺場推廣服務(wù)協(xié)議2篇
- 第2課《濟南的冬天》課時提高練2024-2025學年語文七年級上冊
- 高績效團隊的成功秘密就在會議里!講解材料
- 四年級語文上冊第七單元習作寫信習題課件2新人教版
- 二零二五版交通事故醫(yī)療費用賠償協(xié)議3篇
- 2024年濟寧職業(yè)技術(shù)學院高職單招職業(yè)技能測驗歷年參考題庫(頻考版)含答案解析
- 2024年浙江東方職業(yè)技術(shù)學院高職單招職業(yè)適應性測試歷年參考題庫含答案解析
- - 治療中樞神經(jīng)系統(tǒng)退行性疾病藥
- GB/T 24527-2009炭素材料內(nèi)在水分的測定
- 教練技術(shù)1階段講義一階段版本十一1
- JESD22~B117A中文版完整詳細
- 五大發(fā)電公司及所屬電廠列表及分部精編版
- 2022年新疆青少年出版社有限公司招聘筆試題庫及答案解析
- 《動物生理學》課程思政優(yōu)秀案例
- 高分子材料完整版課件
- DB37∕T 5118-2018 市政工程資料管理標準
- 大氣紅色商務(wù)展望未來贏戰(zhàn)集團年會PPT模板課件
- 住宅工程公共區(qū)域精裝修施工組織設(shè)計(217頁)
評論
0/150
提交評論