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