Cocoa學(xué)習(xí)筆記 設(shè)計模式詳解
Cocoa學(xué)習(xí)筆記 設(shè)計模式詳解是本文要介紹的內(nèi)容,文章中中讓我們從多個方面去了解和學(xué)習(xí)Cocoa的設(shè)計模式,不多說,我們來看內(nèi)容。
枚舉器
類似于java容器類中的iterator,用以遍歷類中的元素
- NSDictionary *Mycollection;
- NSEnumerator *enumerator=[Mycollection objectEnumerator];
- while (instance=[enumerator nextObject]) {
- //
- }
***的objective c引入了快速枚舉,如下所示:
- id instance;
- NSDictionary *Mycollection;
- NSEnumerator *enumerator=[Mycollection objectEnumerator];
- for (instance in Mycollection) {
- //
- }
NSEnumerator類本身也支持快速枚舉,因此可以采用下面的方式反序枚舉容器中的數(shù)據(jù)
- id instance;
- NSArray *Mycollection;
- NSEnumerator *enumerator=[Mycollection objectEnumerator];
- for (instance in [Mycollection reverseObjectEnumerator]) {
- //
- }
要創(chuàng)建自定義的枚舉器,那么就要繼承NSEnumerator類,重要是override nextObject方法
要實現(xiàn)快速枚舉就必須實現(xiàn)NSFastEnumeration協(xié)議,主要是實現(xiàn)以下方法
- - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
執(zhí)行選擇器和延遲執(zhí)行
在cocoa中對象的方法調(diào)用是采用一種消息的方式來執(zhí)行的,因此就需要對象能夠執(zhí)行某個操作,發(fā)送什么消息才能讓對象啟動執(zhí)行某個操作,發(fā)送的消息的內(nèi)容
在cocoa中采用選擇器的方式確定發(fā)送給對象的消息,并且接收消息的對象使用選擇器來選擇調(diào)用哪個方法
- //聲明一個selector并初始化
- SEL aSelector=@selector(application:didChangeStatusBarFrame:);
- //聲明一個selector不初始化
- SEL bSelector;
- //向?qū)ο蟀l(fā)送selector
- id result1=[Mycollection performSelector:aSelector];
- id result2=[Mycollection performSelector:@selector(application:didChangeStatusBarFrame:)];
- //檢測對象是否支持該方法
- if ([Mycollection respondsToSelector:aSelector]) {
- //OK
- }
- //動態(tài)創(chuàng)建類和selector
- id class=[[NSClassFromString(@"TestTableAppDelegate") alloc] init];
- [class performSelector:NSSelectorFromString([NSString stringWithFormat:@"setA%i",i])];
selector的基本原理就是apple的運(yùn)行庫通過在類自身內(nèi)緩沖每個選擇器的IMP來快速搜索對應(yīng)的函數(shù)指針,也可以自己找到對應(yīng)的指針
- [Mycollection methodForSelector:aSelector];
- [NSDictionary instanceMethodForSelector:aSelector];
歸檔與解檔
說白了就是對象序列化
- NSData *data=[NSKeyedArchiver archivedDataWithRootObject:self.window];
- //用戶默認(rèn)數(shù)據(jù)存取
- //存到默認(rèn)數(shù)據(jù)中
- [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"窗口數(shù)據(jù)"];
通過類似的技術(shù)可以把符合協(xié)議的任何對象進(jìn)行歸檔,下面是協(xié)議的定義,***個用于歸檔的時候,第二個用于解檔的時候
- @protocol NSCoding
- - (void)encodeWithCoder:(NSCoder *)aCoder;
- - (id)initWithCoder:(NSCoder *)aDecoder;
- @end
對象要支持歸檔與解檔就必須實現(xiàn)NSCoding協(xié)議
如果對象是繼承于父類,那么在實現(xiàn)NSCoding協(xié)議的時候還必須調(diào)用父類的對應(yīng)方法,如下所示
- @implementation TestClass
- @synthesize test1=_test1;
- static NSString *CodingKeyTest1=@"Test1";
- - (void)encodeWithCoder:(NSCoder *)aCoder{
- [aCoder encodeObject:self.test1 forKey:CodingKeyTest1];
- }
- - (id)initWithCoder:(NSCoder *)aDecoder{
- if (nil!=(self=[super initWithCoder:aDecoder])) {
- [self setTest1:[aDecoder decodeObjectForKey:CodingKeyTest1]];
- }
- return self;
- }
- @end
cocoa單態(tài)模式舉例
書上的例子很多是錯誤的,不知道怎么搞的
- static TestClass *_shareInstance=nil;
- - (void)encodeWithCoder:(NSCoder *)aCoder{
- _test2=@"test";
- self->_test2=@"test2";
- [aCoder encodeObject:self.test1 forKey:CodingKeyTest1];
- }
- - (id)initWithCoder:(NSCoder *)aDecoder{
- if (nil!=(self=[super initWithCoder:aDecoder])) {
- [self setTest1:[aDecoder decodeObjectForKey:CodingKeyTest1]];
- }
- return self;
- }
- (id)hiddenAlloc{
- return [super alloc];
- }
- //單態(tài)模式,不允許創(chuàng)建對象
- (id)alloc{
- return [[self shareInstance] retain];
- }
- (id)new{
- return [self alloc];
- }
- (id)allocWithZone:(NSZone *)zone{
- return [[self shareInstance] retain];
- }
- - (id)copyWithZone:(NSZone *)zone{
- return [[self shareInstance] retain];
- }
- - (id)mutableCopyWithZone:(NSZone *)zone{
- [self copyWithZone:zone];
- return self;
- }
- + (TestClass*)shareInstance{
- if (_shareInstance==nil) {
- _shareInstance=[[super allocWithZone:NULL] init];
- }
- return _shareInstance;
- }
通知
書上的例子很多是錯誤的,不知道怎么搞的
所謂通知也就是消息監(jiān)聽響應(yīng)模式,和MFC的實現(xiàn)有些類似,下面給個例子
要想對象能夠接收消息,那么就必須先把對象注冊到對象通知中心
- typedef struct {
- int id;
- float height;
- unsigned char flag;
- }MyTestStruct;
- //將對象注冊到消息接收泵中
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidChangeSelection:) name:
- @"NSTextViewDidChangeSelection" object:nil];
- //對象接收到消息做出對應(yīng)處理的代碼
- + (void)textViewDidChangeSelection:(NSNotification *)aNotification{
- NSValue *oldValue=[[aNotification userInfo] objectForKey:@"用鍵值查找數(shù)據(jù)"];
- MyTestStruct _teststruct;
- [oldValue getValue:&_teststruct];
- NSLog(@"%f打印結(jié)果咯",_teststruct.height);
- }
- //發(fā)送消息給對象
- - (void) postMessage{
- //發(fā)送通知
- MyTestStruct _teststruct;
- _teststruct.id=0;
- _teststruct.height=10.2;
- NSValue *_value=[NSValue valueWithBytes:&_teststruct objCType:@encode(MyTestStruct)];
- NSDictionary *_dic=[[NSDictionary alloc] initWithObjectsAndKeys:_value, @"用鍵值查找數(shù)據(jù)",nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:@"NSTextViewDidChangeSelection" object:self userInfo:_dic];
- }
委托
說白了,就是另外一個對象的引用
比如A要給B發(fā)送消息,那么A中就保存一個B的實例引用,所以在cocoa的類中很多內(nèi)部都有個無類型的實例變量
- id delegate;
再比如資源文件創(chuàng)建的窗口也有一個delegate,這個delegate要連接到某個類的delegate,那么這個類的委托就可以這樣聲明
- @property(nonatomic,readwrite,assign) IBOutlet id delegate;
也可以定義成符合某種protocol的委托,如下:
- @property(nonatomic,readwrite,assign) IBOutlet id<UITableViewDelegate> delegate;
插座 目標(biāo) 動作
插座變量主要用于連接Nib文件創(chuàng)建的實例,在從nib文件中加載并初始化了所有對象之后,將給加載的每個對象發(fā)送一條如下所示的消息
- - (void)awakeFromNib;
對象接收到這個消息后就會把它的所有插座變量都設(shè)置為在Interface Builder中給它們提供的值
所謂目標(biāo)就是target,在cocoa中很多類都提供了一個名為target的插座變量和對應(yīng)名為action的實例變量
NSControl NSActionCell NSMenuItem實現(xiàn)了setTarget方法來設(shè)置目標(biāo)
任何返回void并且接受一個對象參數(shù)的方法都可以用作動作
用setAction方法來設(shè)置動作
不管發(fā)送動作消息是為了干什么,都是使用NSApplication類的-sendAction:to:from:方法來完成發(fā)送
NSApplication類是一個單態(tài)類,因此發(fā)送動作時一般使用如下
- [[UIApplication sharedApplication] sendAction: to: from: forEvent:];
響應(yīng)者鏈
在cocoa中所有響應(yīng)用戶輸入的對象都是抽象類NSResponder的子類
當(dāng)用戶處理應(yīng)用程序時,cocoa會自動跟蹤用戶的焦點位于何處,當(dāng)前正在接收鍵盤輸入的窗口稱為"關(guān)鍵"窗口,當(dāng)前具有焦點的文檔稱為“主”文檔,主文檔關(guān)聯(lián)的窗口稱為“主”窗口,在cocoa中應(yīng)用程序會自動追蹤關(guān)鍵窗口和主窗口,下面的方法分別獲得引用
- [[UIApplication sharedApplication] keyWindow];//iphone
- [[NSApplication sharedApplication] mainWindow] ;//macos
調(diào)用
大部分人都認(rèn)為selector與消息名稱是一回事,實際上不完全是,selector沒有提供任何類型信息,當(dāng)需要構(gòu)造一個消息的時候就需要知道每個參數(shù)的類型和返回值的類型,這種類型信息就稱為方法簽名(method signature)。
NSMethodSignature類封裝了這種信息,使用示例如下
- MyDocument *mydoc;
- NSMethodSignature *mySig=[mydoc methodSignatureForSelector:@selector(window:shouldDragDocumentWithEvent:from:withPasteboard:) ];
使用NSInvocation可以發(fā)送消息,創(chuàng)建它的實例,配置后可以多次使用,并獲得返回值 ,具體的實例就不寫了,參考下面的網(wǎng)址吧
http://www.cnblogs.com/chenjunbiao/archive/2011/04/20/2022197.html
享元
享元用來封裝非對象數(shù)據(jù),使得可以在上下文中使用,并且在需要大量實例時,享元減少了存儲需求
如 NSNumber NSValue
NSDate;
- NSDecimalNumber;
- NSDate;
- NSCalendarDate;
- NSString;
- NSURL;
- NSFileHandle;
- NSPipe;
- NSAffineTransform;
都是享元
NSColor ,NSFont;這些享元緩存并重用對象
[NSColor redColor];返回同一個共享實例,下一次請求還是用的同樣的一個實例
裝飾器 Decorator
就是對象之間的復(fù)合,減少類的數(shù)量, has-a
用于隱藏復(fù)雜性的模式
包
就是把資源雜七雜八的打包一起
獲得可執(zhí)行程序所在的包
- NSBundle *_budle=[NSBundle bundleForClass:[NSString class]];
動態(tài)加載可執(zhí)行代碼
- NSSearchPathForDirectoriesInDomains //函數(shù)可以獲取所有的包路徑
- _budle=[NSBundle bundleWithPath:@"路徑"];//動態(tài)加載包
- BOOL isLoaded=[_budle load];//強(qiáng)制包的可執(zhí)行代碼鏈接進(jìn)應(yīng)用程序中
- id class1=[_budle classNamed:@"類名"];//訪問包中的類
類簇
Class Cluster模式給復(fù)雜的底層實現(xiàn)提供了一個簡單的接口
類簇的主要動機(jī)就是為了屏蔽內(nèi)部實現(xiàn)的復(fù)雜性,盡量提供簡單的接口
類簇模式利用的技術(shù)依賴于cocoa的兩階段創(chuàng)建模式,兩階段即內(nèi)存分配與初始化
利用兩階段創(chuàng)建,首先從+alloc 返回指向未初始化的新實例的存儲空間指針,然后利用-(id)init方法的某個遍體初始化新實例
因此通過init返回的可能就是公共接口的某個子類的實例,在init方法中首先要釋放掉已經(jīng)分配的抽象基礎(chǔ)類的實例,然后創(chuàng)建可以返回的想要的具體的子類的實例。
類簇的方式提供了簡單的接口,但是復(fù)雜化了子類的創(chuàng)建
管理者模式
顧名思義管理者就是管理其他類的實例的類,cocoa中的NSFileManager NSFontManager NSInputManager NSLayoutManager
在應(yīng)用程序設(shè)計中通常具有一個對象的集合,這些對象需要是唯一的,但是他們并不是單例
例如字體,字體可以有多種不同的字體,但是同一個字體在系統(tǒng)中有一個實例就夠了
小結(jié):Cocoa學(xué)習(xí)筆記 設(shè)計模式詳解的內(nèi)容介紹完了,希望本文對你有所幫助!