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
在应用程序设计中通常具有一个对象的集合,这些对象需要是唯一的,但是他们并不是单例