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學習筆記 設計模式詳解的內容介紹完了,希望本文對你有所協助!