通知中心业务逻辑
- 订阅中心是单例模式,只有一个
- 订阅中心维护书刊的信息,书刊号维护用户的信息
- 用户信息是某种集合(NSDictionary, NSSet, NSArray)
- 观察者(用户)是被动接收消息的,不会影响到订阅中心的发布
抽象接口设计
按照业务逻辑,和涉及的对象,将逻辑理顺,不需要考虑如何实现,先设计接口。例如,通知中心可以这样:
维护订阅信息
//创建订阅号
+ (void)createSubscriptionNumber:(NSString *)subscriptionNumber;
//移除订阅号
+ (void)removeSubscriptionNumber:(NSString *)subscriptionNumber;
维护客户信息
//添加客户到具体的订阅号当中
+ (void)addCustomer:(id <SubscriptionServiceCenterProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber;
//从具体的订阅号当中移除客户
+ (void)removeCustomer:(id <SubscriptionServiceCenterProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber;
发送消息
//发送消息到具体的订阅号当中
+ (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber;
通知中心与用户的消息传递(协议)
消息传递,就让它遵循某类协议,例如订阅的用户
id <SubscriptionServiceCenterProtocol>
订阅中心实例
- NSHashTable对持有的对象是weak引用。这里的例子用来存储”订阅杂志的用户集合”
- NSSet, NSArray, NSDictionary中对持有对象是strong引用
- NSParameterAssert判断不能为nil,否则程序会奔溃
订阅中心创建发行杂志的集合
发行杂志的集合:NSMutableDictionary
+ (void)initialize {
if (self == [SubscriptionServiceCenter class]) {
_subscriptionDictionary = [NSMutableDictionary dictionary];
}
}
获得某订阅号的用户集合
用户集合:NSHashTable
+ (NSHashTable *)existSubscriptionNumber:(NSString *)subscriptionNumber {
return [_subscriptionDictionary objectForKey:subscriptionNumber];
}
创建订阅号
创建订阅号,实质上是创建它的用户集合
+ (void)createSubscriptionNumber:(NSString *)subscriptionNumber {
NSParameterAssert(subscriptionNumber);
//如果这杂志的用户集合不存在,则创建
NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
if (hashTable == nil) {
hashTable = [NSHashTable weakObjectsHashTable];
[_subscriptionDictionary setObject:hashTable forKey:subscriptionNumber];
}
}
移除订阅号
移除订阅号,实质上是移除它的用户集合
+ (void)removeSubscriptionNumber:(NSString *)subscriptionNumber {
NSParameterAssert(subscriptionNumber);
NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
if (hashTable) {
[_subscriptionDictionary removeObjectForKey:subscriptionNumber];
}
}
添加客户到具体的订阅号当中
根据订阅号,取出用户集合,并添加至NSHashTable集合中
+ (void)addCustomer:(id <SubscriptionServiceCenterProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber {
NSParameterAssert(customer);
NSParameterAssert(subscriptionNumber);
NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
[hashTable addObject:customer];
}
从具体的订阅号当中移除客户
根据订阅号,取出用户集合,从NSHashTable集合中删除,这里customer可以为nil,因为该customer可能本来就没有订阅。所以不需要NSParameterAssert判断
+ (void)removeCustomer:(id <SubscriptionServiceCenterProtocol>)customer withSubscriptionNumber:(NSString *)subscriptionNumber {
NSParameterAssert(subscriptionNumber);
NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
[hashTable removeObject:customer];
}
订阅中心发行期刊
从用户集合中,遍历取出每个用户,只要它实现了协议,就可以获得消息
+ (void)sendMessage:(id)message toSubscriptionNumber:(NSString *)subscriptionNumber {
NSParameterAssert(subscriptionNumber);
NSHashTable *hashTable = [self existSubscriptionNumber:subscriptionNumber];
if (hashTable) {
NSEnumerator *enumerator = [hashTable objectEnumerator];
id <SubscriptionServiceCenterProtocol> object = nil;
while (object = [enumerator nextObject]) {
if ([object respondsToSelector:@selector(subscriptionMessage:subscriptionNumber:)]) {
[object subscriptionMessage:message subscriptionNumber:subscriptionNumber];
}
}
}
}
订阅中心的工作过程
// 创建订阅号SCIENCE
[SubscriptionServiceCenter createSubscriptionNumber:SCIENCE];
// 添加订阅的用户到指定的刊物
[SubscriptionServiceCenter addCustomer:self(这里是viewcontroller,只要实现了协议,任何对象都可以)
withSubscriptionNumber:SCIENCE];
// 发行机构发送消息
[SubscriptionServiceCenter sendMessage:@"V1.0" toSubscriptionNumber:SCIENCE];
用户接收消息的实现
- (void)subscriptionMessage:(id)message subscriptionNumber:(NSString *)subscriptionNumber {
NSLog(@"%@ %@", message, subscriptionNumber);
}
框架级别的观察者模式1:NSNotificationCenter
// 添加客户到指定的订阅号中
[[NSNotificationCenter defaultCenter] addObserver:self (用户)
selector:@selector(notificationCenterEvent:)
name:SCIENCE(订阅号)
object:nil];
// 发送信息到指定的订阅号当中
[[NSNotificationCenter defaultCenter] postNotificationName:SCIENCE
object:@"V1.0"];
//通知中心方法
- (void)notificationCenterEvent:(id)sender {
NSLog(@"%@", sender);
}
- (void)dealloc {
// 移除通知中心
[[NSNotificationCenter defaultCenter] removeObserver:self
forKeyPath:SCIENCE];
}
框架级别的观察者模式2:KVO
kVO(key value obeserveing)是一种非常重要的机制,他允许监听对象属性的变化
// 创建订阅中心
self.model = [Model new];
// 客户添加了订阅中心的"name"服务
[self.model addObserver:self (用户)
forKeyPath:@"name" (订阅号)
options:NSKeyValueObservingOptionNew (当订阅号改变时,需要传递什么值给监听器(枚举类型))
context:nil];
// 订阅中心发送消息(通过修改属性值)
self.model.name = @"V1.0";
//KVO方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
NSLog(@"%@", change);
}
- (void)dealloc {
// 移除KVO
[self.model removeObserver:self forKeyPath:@"name"];
}
其他
强引用和弱引用的叙述(转载)
一个对象:类比为一条狗,释放对象:类比为狗要跑掉
- strong类型的指针就像是栓住的狗,只要你用绳子拴住狗,那么狗就不会跑掉.——- (一个对象new过以后,不会自动的释放)
- 如果有5个人都牵着这一条狗(5条绳子栓一只狗)——- (5个strong类型指针指向一个对象.)
- 除非5个绳子都脱落,否则狗是不会跑掉的———— 5个strong指针都=nil,则该对象释放
- weak型指针就像是一个小孩子指着狗喊道:“看,有一只狗在那里”,只要狗一直被拴着,那么小孩子就能看到狗(weak指针)会一直指向它,
- 只要狗的绳子脱落,那么狗就会跑掉,不管有多少的小孩在看着它。
- 只要最后一个strong型指针不再指向对象,那么对象就会被释放,同时所有的weak型指针都将会被清除。
NSEnumerator遍历
NSEnumerator *enumerator = [hashTable objectEnumerator];
id object = nil
while (object = [enumerator nextObject]) {
}
NSParameterAssert
判断不能为nil,否则程序会奔溃
判断对象存在与否
//对象存在
if (hashTable) {...}
//对象不存在
if (hashTable == nil) {....}
initialize
每个对象都具有的初始化方法,只执行一次
+ (void)initialize {...}
KVC (key value coding)
使用valueforKey:获取Student对象的name。
NSString *name = [Student valueForKey :@"name"];
使用setValue:ForKey:设置student对象的name。
[Student setVlue:@"zhangjl" forKey:@"name"];
支持通过键路径(key path)进行访问和赋值。比如:利用键路径对Student对象的Card对象的no属性进行访问和赋值
[Student setValue:@"1234" forKeyPath: @"card.no"];
[Student valueForKeyPath:@"card.no"]