標籤:
類別Category1,概述
為現有類添加新的方法,這些新方法的Objective-C的術語為“類別”。
2,用法
a,聲明類別
@interface NSString(NumberConvenience)
-(Number *)lengthAsNumber;
@end//NumberConvenience
這裡類別的名稱就叫NumberConvenience,新的類方法為lengthAsNumber.
b,實作類別別
@implementation NSString(NumberConvenience)
-(NSNumber *)lengthAsNumber
{
unsigned int length= [self length];
return([NSNumber numberWithUnsignedInt:length]);
}//lengthAsNumber
@end//NumberConvenience
c,類別的使用
如下所示,根據上面定義的類別:NumberConvenience,所有的NSString對象都能響應lengthAsNumber
NUMutableDictionary *dict;
dict = [NSMutableDictionary dictionary];
[dict setObject:[@"hello" lengthAsNumber]
forKey:@"hello"];
[dict setObject:[@"iLikeFish" lengthAsNumber]
forKey:@"iLikeFish"];
[dict setObject:[@"Once upon a time" lengthAsNumber]
forKey:@"Once upon a time"];
NSLog(@"%@",dict);
以上代碼將"hello""iLikeFish""Once upon a time"三個字串與其對應的長度放在了可變字典dict中。
d,類別的不足和作用
類別存在以下不足:一是不能向類別中添加執行個體變數,二是類別的方法與現有的方法可能存在重名現象,當出現重名時,類別具有更高的優先順序。
類別的作用有:
一是將類的實現分散到多個不同的檔案或多個不同的架構中。
解釋:在通常情況下,我們可以將類的介面放入標頭檔(.h)中,將實現放在實現檔案中(.m),但不能將@implementation放入多個.m檔案中,但是類別可以。
以下代碼來解釋此條作用。
//NSWindows.h
@interface NSWindow:NSResponder
@interface NSWindow(NSKeyboardUI)
@interface NSWindow(NSToolbarSupport)
@interface NSWindow(NSDrag)
@interface NSWindow(NSCarbonExtensions)
@interface NSObject(NSWindowDelegate)
以上是系統內建的NSWidows
以下代碼完整的表述了分散實作類別別的方法
//.h檔案
#import <Foundation/Foundation.h>
@interface CategoryThing:NSObject{
int thing1;
int thing2;
int thing3;
}
@end //CategoryThing
聲明完執行個體變數後,要繼續聲明類別,這與普通的類有所不同:普通的類將所有的方法都放在介面中聲明,類別將每個類別的方法單獨聲明。如下:
@interface CategoryThing(Thing1)
-(void)setThing1:(int)thing1;
-(int)thing1;
@end //CategoryThing(Thing1)
@interface CategoryThing(Thing2)
-(void)setThing2:(int)thing2;
-(int)thing2;
@end//CategoryThing(Thing2)
@interface CategoryThing(Thing3)
-(void)setThing3:(int)thing3;
-(int)thing3;
@end //CategoryThing(Thing3)
然後,將interface CategoryThing(Thing1),interface CategoryThing(Thing2),interface CategoryThing(Thing3)分別放在Thing1.m,Thing2.m和Thing3.m中實現,如下:
Thing1.m中包含了Thing1類的實現:
#import "CategoryThing.h"
@implementation CategoryThing(Thing1)
-(void)setThing1:(int)t1
{
thing1=t1;
}//setThing1
-(int)thing1
{
return(thing1);
}//thing1
@end//CategoryThing
Thing2.m中包含了Thing2類的實現:
#import "CategoryThing.h"
@implementation CategoryThing(Thing2)
-(void)setThing2:(int)t2
{
thing2=t2;
}//setThing2
-(int)thing2
{
return (thing2);
}//thing2
@end
二是對私人方法的前向引用。
當我們想建立一個類的私人化方法,在OB中好像是不可能的,但是我們可以使用類別來實現。
在OB中可以存在以下情況:我們不在介面中聲明某個方法,但我們在實現檔案中實現了該方法,並且該方法是可以被調用的,缺點是編譯器會報錯。
此時,我們就可以使用類別,將該類定義為類別,那麼,其中我們就可以不在介面中聲明,而在實現中實現並可調用,這種方法被開發人員稱作為OB中的私人方法。實現過程如下所示:
@interface Car(PrivateMethods)
-(void)moveTireFromPosition:(int)pos1
toPosition:(int)pos2;
@end//PrivateMethods
以下是方法實現細節
@implementation Car(PrivateMethods)
-(void)moveTireFromPosition:(int)pos1
toPosition:(int)pos2
{
//.....TheImplementation of Methods
}//moveTireFromPosition:toPosition
-(void)rotateTires{
//.....The implementation of methods
}//rotateTires
三是向對象添加非正式協議(informal protocol)。
先講講委託(delegation),看了蘋果的開發人員文檔後,我對委託的理解是:將已經固化的一些訊息響應操作分離出其原有的動作的一種方法。比如,原本關閉一個視窗是一個已經固化了的操作並不需要我們去實現其具體的細節,但是當我們需要在關閉視窗時還要幹點其它的事情,比如儲存資料,關閉串連等,就需要介面來幫忙。
它是Cocoa中的類經常使用的一種的技術,委託是一種對象,另一個類的對象會要求委派物件執行它的某些操作。委託是一種設計模式,委託者對象(the delegating object)擁有對委託(the delegate)的引用,委託者對象可以向委託發送訊息,委託(the delegate)根據訊息來響應某些動作,並通過傳回值來反饋自己響應某些動作的結果。
在蘋果開發人員文檔中這樣解釋委託:
委託是一個對象,當另一個對象遇到一個事件時,代表或者與另一個對象協作來完成某件事。通常情況下,委託者對象(the deletaging object)通常是一個從NSResponder繼承而來的用來響應使用者事件的響應對象(responsder object)。而委託(the delegate)是授權控制使用者介面的對象,或者最起碼要以特定的應用方式對事件進行解釋。
比如一些視窗對象,早就設計好了主要是用來對譬如放大縮小視窗,關閉視窗,已最小化的視窗等事件進行響應,這種封閉性的設計肯定會對於這些事件會不會千萬其它的影響全然不知,比如將一個視窗關閉,視窗對象早就設計好了如何關閉視窗等等,但是對於關閉視窗這個動作會不會影響資料的儲存?網路連接的中斷?等等其它狀態造成影響,視窗對象完全不知。委託的存在為我們定製的對象和現成的對象之間架起了溝通的橋樑。
通常委託者對象(the delegating object)有一個outlet或者property,通常名字為delegate;如果是outlet,則它另外還含有一個存取outlet的方法。通常還有一個或更多方法的聲明,通常是沒有實現的,構成了非正式協議或者正式協議。如果是正式協議,則方法聲明為optional,通常使用正式協議的方法更為普遍。如所示,為委託的圖示樣本:
協議中的方法通常都標註了需要代理處理的重要的事件。委託者對象(the delegating object)將這些事件或者即將發生的事件傳遞給委託(delegate object),以請求delegate object有所反映。比如,當一個使用者點擊視窗的關閉按鈕時,視窗對象會改善一個視窗關閉訊息給代理,這給代理對象一個機會來決定在關閉視窗時還需要繼續完成的事情,如所示,視窗對象還需要將資料進行儲存。
但是委託者對象delegating object只發送委託delegate定義了的訊息。因為這樣才可以讓NSObject對象的respondsToSelector方法被調用。
通常委託方法的形式比較方便,通常沒有UI,NS等首碼,並且名稱中常含有動詞,如open,或者時態副詞,如will,has.心下展示了一些有傳回值和沒有傳回值的委託方法:
- (BOOL)application:(NSApplication *)sender |
openFile:(NSString *)filename; // NSApplication |
- (BOOL)application:(UIApplication *)application |
handleOpenURL:(NSURL *)url; // UIApplicationDelegate |
- (UITableRowIndexSet *)tableView:(NSTableView *)tableView |
willSelectRows:(UITableRowIndexSet *)selection; // UITableViewDelegate |
- (NSRect)windowWillUseStandardFrame:(NSWindow *)window |
defaultFrame:(NSRect)newFrame; // NSWindow |
這些方法可以打斷接下來發生的事件,或者改變一個建議值。甚至可以定義一個接下來發生的事件,比如,當一個代理delegate實現了applicationShouldTerminate方法後,可以返回一個NSTerminateLater對象。
以下是一些沒有傳回值的委託方法,它們是比較純粹的訊息。
- (void) tableView:(NSTableView*)tableView |
mouseDownInHeaderOfTableColumn:(NSTableColumn *)tableColumn; // NSTableView |
- (void)windowDidMove:(NSNotification *)notification; // NSWindow |
- (void)application:(UIApplication *)application |
willChangeStatusBarFrame:(CGRect)newStatusBarFrame; // UIApplication |
- (void)applicationWillBecomeActive:(NSNotification *)notification; // NSApplication |
但是我們來注意一下中每二個和第四個方法,其中的參數為NSNotifications對象,這意味著,當這些方法被調用時,會傳遞一些特定的訊息(particular notification)給這些方法,比如windowDidMove:方法就與windows的通知
NSWindowDidMoveNotification
.存在聯絡。
如何使用委託?
使用非正式協議
使用正式協議,如下所示樣本:
//
// main.m
// delegationTest
//
// Created by dbseti on 16/7/21.
// Copyright © 2016年 dbseti. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol SecProtocol <NSObject>
-(void)payoff;
-(void)tel;
@end
@interface Secret:NSObject<SecProtocol>
@end
@implementation Secret
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)payoff{
NSLog(@"secret payoff");
}
-(void)tel{
NSLog(@"secret tel");
}
@end
@interface Boss:NSObject
@property (nonatomic,retain) id <SecProtocol> delegate;
-(void)manager;
-(void)teach;
@end
@implementation Boss
@synthesize delegate=_delegate;
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)manager{
NSLog(@"Boss manage");
}
-(void)teach{
NSLog(@"Boss teach");
}
-(void)payoff{
[_delegate payoff];
}
-(void)tel{
[_delegate tel];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Secret *sec=[[Secret alloc]init];
Boss *b=[[Boss alloc]init];
b.delegate=sec;//這一步很關鍵,它將sec賦給b的代理delegate,實際上還是由b的成員來執行了這個操作
[b teach];
[b manager];
[b payoff];
[b tel];
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
以片顯示了他們的邏輯關係。
什麼協議都不使用
背影條件:有對象A和對象B,對象A有一個方法p,對象B沒有方法p,但是它想使用方法p,它讓A去調用p方法,這個是最基本的委託。(其實按我個人的理解,這個根本就是調用A對象中方法p)沒有太多的新的概念和方法。
@interface A:NSObject
-(void)p;
@end
@implementation A
-(void)p
{
NSLog(@"I am A‘s method p");
}
@end
@interface B:NSObject
{
A* delegate;
}
@property (nocopy,retain) A* delegate;
@end
@implementation B
@synthesize A*delegate;
@end
void main(){
B * b=[[B alloc] init];
A * a=[[A alloc] init];
b.delegate=a;
[b.delegate p];
}
還有一種方式,如下所示A類包含B類的一個對象和一個print方法,在A類實現的時候,多了一個方法viewDidLoad和一個特性delegate;B類包含一個類型為id的delegate
@interface A:NSObject{
B *b;
}
-(void)print;
@end
@implementation A
@synthesize delegate;
-(void)viewDidLoad{
b=[[B alloc]init];
b.delegate=self;
}
-(void)print{
NSLog(@"print was called");
}
@interface B:NSObject{
id delegate;}
@property(nonmatic,retain) id delegate;
@end
@implementation B
-(void)callPrint{
[self.delegate print];
}
@end
Objective-C日記-之類別Category