类别Category
1,概述
为现有类添加新的方法,这些新方法的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