协议与分类:
OC语言中的协议类似于java语言中的接口,OC不支持多重继承,因此我们把某个类应该实习那的一系列方法定义在其协议里。协议最常见的是委托模式,
分类也是OC语言的一项重要有v眼特性。利用好分类机制,我们无需继承子类即可为当前类添加方法。而在其他编程语言中,需要继承子类来实现。
对象之间经常需要通信,OC中有名为“委托模式”的编程设计模式来实现对象间的通信。
委托模式的主旨是:定义一套接口,某对象若想接受另一个对象的委托,遵从此接口,使其成为“委托对象”。而这“另一个对象”可以给委托对象回传一些消息,使其在发生相关消息时通知委托对象。
次模式可以将数据与业务逻辑解耦。、
@protocol EOCNetworkFetcherDelegate
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveDate:(NSDate *)data;
- (void)networkFetcher:(EOCNetworkFetcher *)fether didFaillWithError:(NSError *)error;
@end
@interface EOCNetworkFetcher : NSObject
@property (nonatomic, weak) id<EOCNetworkFetcherDelegate> delegate;
@end
实现委托协议的方法是声明某个类遵从委托协议,然后把协议中想实现的方法在类里实现出来。若类药遵从委托协议,可以在其接口中声明,也可以在class-continuation分类中声明。如果要向外界公布此类实现了某协议,那么就在接口中声明,而如果这个歌协议是委托协议,通常只会在类的内部使用,这种情况下都是在class-continuation分类里声明
委托协议中的方法一般都是“可选的”,因为扮演“受委托者”角色的这个对象未必关心其中所有的方法。为了指明可选方法,委托协议经常使用@optional关键字来标注方法。
如果要在委托对象上调用可选方法,那么必须提前使用类型信息查询方法判断这个委托对象是否能响应相关选择子。这样的话,delegate对象就可以完全按照其需要实现委托协议中的方法了,不用担心因为哪个方法没实现导致程序出问题,即使没有配置委托对象,程序也能照常运行,因为nil发送消息使if语句的值称为false
delegate中的方法名也一定要起的很恰当才行,方法名应该准确描述当前发生的事件以及delegate对象为何要获知此事件。调用delegate中的方法时,把发起委托的实例也一并传入方法中,这样,delegate对象在实现相关方法时,就能根据传入的实例分别执行不同的代码块。delegate中的方法也可以用于从获取委托对象中获取信息。
delegate里的方法也可以用于从获取委托对象中获取信息。
也可以用协议定义一套接口,令某类经由该接口获取其所需的数据。委托模式的这一用法旨在向类提供数据,孤儿又称“数据源模式”。此模式中,信息从数据源流向类,在常规的委托模式中,信息从类流向委托者。
若有必要,可以实现含有位段的结构体,将委托对象是否能响应相关协议方法这一信息缓存至其中
类中经常容易填满各种方法,而这些方法的代码则全部堆在一个巨大的时间文件里。我们可以通过OC的分类机制,把类代码按逻辑划入几个分区中。
// EOCOPerson.h
// text。12.14
//
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCOPerson : NSObject
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSString* lastName;
@property (nonatomic, strong, readonly) NSSet* friends;
- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString *)lastName;
- (void)addFriend:(EOCOPerson *)person;
- (void)removeFriend:(EOCOPerson *)person;
- (BOOL)isFriendsWith:(EOCOPerson *)person;
-(void)performnDaysWork;
- (void)takeVacationFromWork;
- (void)goToTheCinema;
- (void)goToSportGames;
@end
NS_ASSUME_NONNULL_END
// EOCOPerson.h
// text。12.14
//
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCOPerson : NSObject
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSString* lastName;
@property (nonatomic, strong, readonly) NSSet* friends;
- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString *)lastName;
@end
@interface EOCOPerson(Friendsship)
- (void)addFriend:(EOCOPerson *)person;
- (void)removeFriend:(EOCOPerson *)person;
- (BOOL)isFriendsWith:(EOCOPerson *)person;
@end
@interface EOCOPerson(Work)
-(void)performnDaysWork;
- (void)takeVacationFromWork;
@end
@interface EOCOPerson(Play)
- (void)goToTheCinema;
- (void)goToSportGames;
@end
NS_ASSUME_NONNULL_END
// EOCOPerson+Friendship.h
// text。12.14
//
//
#import "EOCOPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface EOCOPerson_Friendship : EOCOPerson
- (void)addFriend:(EOCOPerson *)person;
- (void)removeFriend:(EOCOPerson *)person;
- (BOOL)isFriendsWith:(EOCOPerson *)person;
@end
NS_ASSUME_NONNULL_END
// EOCOPerson+Friendship.m
// text。12.14
//
//
#import "EOCOPerson+Friendship.h"
@implementation EOCOPerson_Friendship
- (void)addFriend:(EOCOPerson *)person{
return 0;
}
- (void)removeFriend:(EOCOPerson *)person{
return 0;
}
- (BOOL)isFriendsWith:(EOCOPerson *)person{
return YES;
}
@end
分类机制通常用于向无源码的既有类中新增功能。分类中的方法是直接添加在类里面的,他们就好比这个类中的固有方法。将分类方法加入类中这一操作是在运行期系统加载分类时完成的。如果类中本来就有此方法,分类又实现了一次,那么分类中的方法就会覆盖原来那一份实现代码。
属性是封装数据的方式。我们要尽量避免从坟里里声明属性。因为除了class-continuation分类之外,其他分类都无法向类中新增实例变量,因此他们无法把实现属性所需的实例变量合成出来。
// EOCOPerson.h
// text。12.14
//
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCOPerson : NSObject
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSString* lastName;
- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString *)lastName;
@end
@interface EOCOPerson(Friendsship)
@property (nonatomic, strong) NSArray* friends;
- (void)addFriend:(EOCOPerson *)person;
- (void)removeFriend:(EOCOPerson *)person;
- (BOOL)isFriendsWith:(EOCOPerson *)person;
@end
@interface EOCOPerson(Work)
-(void)performnDaysWork;
- (void)takeVacationFromWork;
@end
@interface EOCOPerson(Play)
- (void)goToTheCinema;
- (void)goToSportGames;
@end
NS_ASSUME_NONNULL_END
类中经常包含一些无需对外公布的方法和实例变量。可以将这些内容写为私有,开发者不依赖他们。OC的动态消息系统决定了其不可能实现真正的私有方法或者私有实例变量,
@interface EOCOPerson()
@end
@interface EOCOPerson() {
NSString* _anInstanceVariable;
}
- (void)addPerson:(EOCOPerson *)person;
@end
协议定义了一系列方法,遵从次协议的对象以应该实现它们。(如果这些方法不可选,那么就必须实现)我们可以利用协议把自己所写的API之中的实现细节隐藏起来,将返回的对象设计为遵从此协议的纯id类型。这样的话,想要隐藏的类名就不会出现在API之中了。若是接口背后有多个不同的实现类,而我们又不想具体指明使用那歌类,那么可以考虑这个办法,因为有时候这些类可能会变,有时候它们又无法容纳于标准的类的继承体系中。