ios 发起通知的时候接收对象被c 释放对象了怎么处理

ios的内存警告怎么处理_百度知道2917人阅读
如果在一个类中想要执行另一个类中的方法可以使用通知
1.创建一个通知对象:使用notificationWithName:object: 或者 notificationWithName:object:userInfo:
&&& NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoadFailed(connection.imageURL)
&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& & object:self
&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& &&& userInfo:[NSDictionary dictionaryWithObjectsAndKeys:error,@&error&,connection.imageURL,@&imageURL&,nil]];
这 里需要注意的是,创建自己的通知并不是必须的。而是在创建自己的通知之前,采用NSNotificationCenter类的方 法&postNotificationName:object: 和 postNotificationName:object:userInfo:更加便利的发出通知。这种情况,一般使用NSNotificationCenter的类方法defaultCenter就获得默认的通知对象,这样你就可以给该程序的默认通知中心发送通知了。注意:每一个程序都有一个自己的通知中心,即NSNotificationCenter对象。该对象采用单例设计模式,采用defaultCenter方法就可以获得唯一的NSNotificationCenter对象。
注意:NSNotification对象是不可变的,因为一旦创建,对象是不能更改的。
2.注册通知:addObserver:selector:name:object:
可以看到除了添加观察者之外,还有其接收到通知之后的执行方法入口,即selector的实参。因此为了进行防御式编程,最好先检查观察者是否定义了该方法。例如:添加观察者代码有
[[NSNotificationCenter defaultCenter] addObserver:self
&&& selector:@selector(aWindowBecameMain:)
&&& name:NSWindowDidBecomeMainNotification object:nil];
这里保证了self定义了aWindowBecameMain:方法。而对于一个任意的观察者observer,不能保证其对应的selector有aWindowBecameMain:,可采用[observer
respondsToSelector:@selector(aWindowBecameMain:)]] 进行检查。所以完整的添加观察者过程为:
if([observer respondsToSelector:@selector(aWindowBecameMain:)])
&& &&& &[[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(aWindowBecameMain:) name:NSWindowDidBecomeMainNotificationobject:nil];
注 意到addObserver:selector:name:object:不仅指定一个观察者,指定通知中心发送给观察者的消息,还有接收通知的名字,以 及指定的对象。一般来说不需要指定name和object,但如果仅仅指定了一个object,观察者将收到该对象的所有通知。例如将上面的代码中
name改为nil,那么观察者将接收到object对象的所有消息,但是确定不了接收这些消息的顺序。如果指指定一个通知名称,观察者将收到它每次发出 的通知。例如,上面的代码中object为nil,那么客户对象(self)将收到任何对象发出NSWindowDidBecomeMainNotification通知。如果既没有指定指定object,也没有指定name,那么该观察者将收到所有对象的所有消息。
3.发送通知:postNotificationName:object:或者performSelectorOnMainThread:withObject:waitUntilDone:
例如程序可以实现将一个文本可以进行一系列的转换,例如对于一个实例、RTF格式转换成ASCII格式。而转换在一个类(如Converter类)的对象中得到处理,在诚寻执行过程中可以加入或者删除这种转换。而且当添加或者删除Converter操作时,你的程序可能需要通知其他的对象,但是这些Converter对象并不需要知道被通知对象是什么,能干什么。你只需要声明两个通知,&ConverterAdded&
和 &ConverterRemoved&,并且在某一事件发生时就发出这两个通知。
当一个用户安装或者删除一个Converter,它将发送下面的消息给通知中心:
[[NSNotificationCenter defaultCenter]
&&& postNotificationName:@&ConverterAdded& object:self];
[[NSNotificationCenter defaultCenter]
&&& postNotificationName:@&ConverterRemoved& object:self];
通知中心将会区分它们对象对这些通知感兴趣并且通知他们。如果除了关心观察者的通知名称和观察的对象,还关心其他之外的对象,那么就把之外的对象放在通知的可选字典中,或者用方法postNotificationName:object:userInfo:。
而采用performSelectorOnMainThread:withObject:waitUntilDone:则是直接调用NSNotification的方法postNotification,而postNotificationName和object参数可以放到withObject的实参中。例如:
[[NSNotificationCenter
defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification&waitUntilDone:YES];//注意这里的notification为自定义的一个通知对象,可定义为NSNotification*
notification = [NSNotification notificationWithName:@&ConverterAdded&object:self];//那么它的作用与上面的一致
4.移除通知:removeObserver:和removeObserver:name:object:
其中,removeObserver:是删除通知中心保存的调度表一个观察者的所有入口,而removeObserver:name:object:是删除匹配了通知中心保存的调度表中观察者的一个入口。
这个比较简单,直接调用该方法就行。例如:
[[NSNotificationCenter defaultCenter] removeObserver:observer name:nil object:self];
注意参数notificationObserver为要删除的观察者,一定不能置为nil。
PS:这里简单说一下通知中心保存的调度表。通知中心的调度表是给一些观察者指定的一些通知集。一个通知集是通知中心发出的通知的子集。每个表的入口包含:
通知观察者(必须要的)、通知名称、通知的发送者。
下图表示通知集中指定的通知的调用表入口的四种类型:
下图表示四种观察者的调度表
最后,提醒一下观察者收到通知的顺序是没有定义的。同时通知发出和观察的对象有可能是一样的。通知中心同步转发通知给观察者,就是说&postNotification:
方法直到接收并处理完通知才返回值。要想异步的发送通知,可以使用NSNotificationQueue。在多线程编程中,通知一般是在一个发出通知的那个线程中转发,但也可能是不在同一个线程中转发通知。
在对象间传递信息的标准方法是消息传递-即一个对象调用另一个对象的方法。然而,消息传递要求发送消息的对象知道消息的接收者,以及它可以响应什么 消息。这个要求对于委托消息和其它类型的消息是可以的。有些时候,我们不希望两个对象之间具有这种紧密的耦合-特别值得注意的原因是它会把本来独立的子系 统联结在一起。而且这种要求也是不切实际的,因为它需要把应用程序中很多全然不同的对象之间建立硬编码的连接。
对于不能使用标准的消息传递的场合,Cocoa提供了通告广播模型。通过通告机制,一个对象可以通知其它对象自己正在干什么。在这个意义上,通告机 制类似于委托,但是它们之间的区别是很重要的。委托和通告的关键区别在于前者是一对一的通讯路径(在向外委托任务的对象和被委托的对象之间)。而通告是潜 在的一对多的通讯方式-也就是一种广播。一个对象只能有一个委托,但可以有很多观察者,因为通告的接收者是未知的。对象不必知道那些观察者是什么对象。任何对象都可以间接地通过通告来观察一个事件,并通过调整自己的外观、行为、和状态来响应事件。通告是一种在应用程序中进行协调和聚合的强大机制。
通告机制是如何工作的?这在概念上相当直接。在进程中有一个称为通告中心的对象,充当通告的信息交换和广播中心。在应用程序的其它地方,需要知道某 个事件的对象在通告中心进行注册,让它知道当该事件发生时,自己希望得到通知。这种场景的一个例子是当一个弹出式菜单被选择时,控制器对象需要知道这个事 件,以便在用户界面上对反映这个变化。当事件发生时,处理该事件的对象向通告中心发出一个通告,然后通告中心会将它派发给所有的相关的观察者。图5-8描述了这种机制。
请注意:通告中心同步地将通告派发给它的观察者。发出通告的对象直到所有的通告被发出后,才重新获得程序的控制权。如果需要以异步的方式发送通告,必须使用通告队列(参见)。通告队列在对特定的通告进行延迟处理,并根据某些具体的条件将类似的通告进行组合之后,才将通告发给通告中心。
图5-8&&公布和广播通告
任何对象都可以发出通告,也可以在通告中心注册为通告的观察者。发出通告的对象、通告中包含的对象、还有通告的观察者可以是不同的对象,也可以是同 一个对象(用同一个对象作为通告的发送者和观察者有它的用处,比如用于空闲处理方面)。发送通告的对象不需要知道通告观察者的任何信息。另一方面,观察者 至少需要知道通告的名称和通告对象中封装的字典的键(&部分描述了通告对象的是有什么组成的)。
进一步阅读:&如果需要有关通告机制的完全讨论。
本部分包含如下主要内容:
何时以及如何使用通告
和委托一样,通告机制是实现应用程序中的对象间通讯的好工具。它使应用程序中的对象可以了解其它地方发生的改变。一般地说,一个对象注册为通告的观 察者,是因为它希望在相应的事件发生后或即将发生时进行调整。举例来说,如果一个定制视图希望在窗口调整尺寸的时候改变自己的外观,则可以观察窗口对象发 出的NSWindowDidResizeNotification通告。通告也允许在对象间传递信息,因为通告中可以包含一个与事件相关的字典。
但是,通告和委托之间是不同的,这些差别也导致这两种机制应该用于不同的地方。在早些时候提到,通告模型和委托模型的主要区别在于前者是广播机制,而委托是一对一的关系。每种模型都有自己的优点,通告机制的优点如下:
发出通告的对象不需要知道观察者的标识。
应用程序并不受限于Cocoa框架声明的通告;任何类都可以声明通告,其实例可以发布通告。
通告并不限于应用程序内部的通讯;通过分布式通告,一个进程可以将发生的事件通知另一个进程。
但是,一对一的委托模型也有自己的优点。委托有机会通过将值返回给进行委托的对象来影响事件。另一方面,通告的观察者必须发挥更为被动的作用,在响应事件时,它只能对自身及其环境产生影响。通告方法必须具有如下形式的签名:
- (void)notificationHandlerName:(NSNotification *);
这个要求使观察对象无法以任何直接的方式影响原来的事件。但是,委托通常可以影响进行委托的对象对事件的处理方式。而且,Application Kit对象的委托自动注册为其通告的观察者。您只需要实现框架类定义的通告方法,就可以接收通告。
在Cocoa中,通告机制并不是观察对象状态变化的唯一选择,在很多情况下甚至都不是最好的选择。Cocoa绑定技术,特别是为其提供支持的键-值 观察(KVO)和键-值绑定(KVB)协议,也可以使应用程序中的对象观察其它对象性质的变化。绑定机制比通告机制更为有效。在绑定机制中,被观察对象和 观察对象直接进行通讯,不需要像通告中心这样的中间对象。而且,绑定机制不会像常见的通告那样,因为处理无观察者的变化而对性能产生不利影响。
但是在某些场合中,选择通告比选择绑定更为合理。您可能希望观察事件,而不是对象性质的改变;或者,有些时候遵循KVO和KVB是不适合实际情况的,特别是当需要发送和观察的通告很少的时候。
即使在适合使用通告的场合下,您也应该知道它对性能的潜在影响。通告发出之后,最终会通过本地的通告中心同步地派发给观察对象。不管通告的发送是同 步的还是异步的,这个过程都是要发生的。如果有很多观察者,或者每个观察者在处理通告时都做很多工作,您的程序就会有明显的延迟。因此,您应该小心,不要 过度或低效地使用通告。最后,下面这些关于通告用法的原则应该有帮助:
对应用程序应该观察的通告有所选择。
注册通告时,要具体到通告的名称和发送的对象。
尽可能高效地实现处理通告的方法。
避免添加或移除很多观察者;通过一些“中间的”观察者将通告的结果传递给它们可以访问的对象要好得多。
进一步阅读:如果您需要有效使用通告的详细信息,请参见Cocoa性能指南中的&通告&部分。
通告是一个对象,是NSNotification的一个实例。该对象封装了与事件有关的信息,比如某个窗口获得了焦点,或者某个网络连接关闭了。当事件发生时,负责处理该事件的对象会向通告中心发出一个通告,通告中心则立刻将通告广播给所有注册的对象。
NSNotification对象中包含一个名称、一个对象、还有一个可选的字典。名称是标识通告的标签;对象是指通告的发送者希望发给通告观察者的对象(它通常是通告发送者本身),类似于委托消息的发送者对象,通告的接收者可以向该对象查询更多的信息;字典则用于存储与事件有关的信息。
通告中心负责发送和接受通告。它将通告通知给所有符合条件的观察者。通告信息封装在NSNotification对象中。客户对象在通告中心中将自己注册为特定通告的观察者。当事件发生时,对象向通告中心发出相应的通告。而通告中心会向所有注册过的观察者派发消息,并将通告作为唯一的参数进行传递。通告的发送对象和观察对象有可能是同一个对象。
Cocoa框架中包含两种类型的通告中心:
通告中心(NSNotificationCenter的实例),管理单任务的通告。
分布式通告中心(NSDistributedNotificationCenter的实例),管理单台计算机上多任务之间的通告。
请注意,NSNotificationCenter和很多其它的Foundation类不同,不能和其在Core Foundation中对应的结构(CFNotificationCenterRef)进行自由桥接。
NSNotificationCenter
每个任务都有一个缺省的通告中心,您可以通过NSNotificationCenter的defaultCenter类方法来进行访问。通告中心在单任务中处理通告。如果需要在同一个机器的不同任务之间进行通讯,可以使用分布式通告中心。
通告中心同步地将通告发送给观察者。换句话说,在发出一个通告时,在所有的观察者接收和处理完成通告之前,程序的控制权不会返回给发送者。如果需要异步发送通告,可以使用通告队列,这在部分中进行描述。
在多线程的应用程序中,通告总是在发送的线程中传送,这个线程可能不同于观察者注册所在的线程。
NSDistributedNotificationCenter
每个任务都有一个缺省的分布式通告中心,您可以通过NSDistributedNotificationCenter的defaultCenter类方法来访问。这个分布式通告中心负责处理同一个机器的不通任务之间的通告。如果需要实现不同机器上的任务间通讯,请使用分布式对象。
发送分布式通告是一个开销昂贵的操作。通告会被发送给一个系统级别的服务器,然后再分发到注册了该分布式通告的对象所在的任务中。发送通告和通告到达另一个任务之间的延迟是很大的。事实上,如果发出的通告太多,以致于充满了服务器的队列,就可能被丢弃。
分布式通告通过任务的运行循环来分发。任务必须运行在某种“常见”模式的运行循环下,比如NSDefaultRunLoopMode模式,才能接收分布式通告。如果接收通告的任务是多线程的,则不要以通告会到达主线程作为前提。通告通常被分发到主线程的运行循环上,但是其它线程也可以接收通告。
尽管常规的通告中心允许任何对象作为通告对象(也就是通告封装的对象),分布式通告中心只支持将NSString对象作为它的通告对象。由于发出通告的对象和通告的观察者可能位于不同的任务中,通告不能包含指向任意对象的指针。因此,分布式通告中心要求通告使用字符串作为通告对象。通告的匹配就是基于这个字符串进行的,而不是基于对象指针。
NSNotificationQueue对象(或者简单称为通告队列)的作用是充当通告中心(NSNotificationCenter的实例)的缓冲区。通告队列通常以先进先出(FIFO)的顺序维护通告。当一个通告上升到队列的前面时,队列就将它发送给通告中心,通告中心随后将它派发给所有注册为观察者的对象。
每个线程都有一个缺省的通告队列,与任务的缺省通告中心相关联。图5-9展示了这种关联。您可以创建自己的通告队列,使得每个线程和通告中心有多个队列。
图5-9&&通告队列和通告中心
聚结的通告
NSNotificationQueue类为Foundation Kit的通告机制增加了两个重要的特性:即通告的聚结和异步发送。聚结是把和刚进入队列的通告相类似的其它通告从队列中移除的过程。如果一个新的通告和已 经在队列中的通告相类似,则新的通告不进入队列,而所有类似的通告(除了队列中的第一个通告以外)都被移除。然而,您不应该依赖于这个特殊的聚结行为。
您可以为enqueueNotification:postingStyle:coalesceMask:forModes:方法的第三个参数指定如下的一或多个常量,指示简化的条件:
NSNotificationNoCoalescing
NSNotificationCoalescingOnName
NSNotificationCoalescingOnSender
您可以对NSNotificationCoalescingOnName和NSNotificationCoalescingOnSender常量进行位或操作,指示Cocoa同时使用通告名称和通告对象进行聚结。那样的话,和刚刚进入队列的通告具有相同名称和发送者对象的所有通告都会被聚结。
异步发送通告
通过NSNotificationCenter类的postNotification:方法及其变体,您可以将通告立即发送给通告中心。但是,这个方法的调用是同步的:即在通告发送对象可以继续执行其所在线程的工作之前,必须等待通告中心将通告派发给所有的观察者并将控制权返回。但是,您也可以通过NSNotificationQueue的enqueueNotification:postingStyle:和enqueueNotification:postingStyle:coalesceMask:forModes:方法将通告放入队列,实现异步发送,在把通告放入队列之后,这些方法会立即将控制权返回给调用对象。
Cocoa根据排队方法中指定的发送风格和运行循环模式来清空通告队列和发送通告。模式参数指定在什么运行循环模式下清空队列。举例来说,如果您指定NSModalPanelRunLoopMode模式,则通告只有当运行循环处于该模式下才会被发送。如果当前运行循环不在该模式下,通告就需要等待,直到下次运行循环进入该模式。
向通告队列发送通告可以有三种风格:NSPostASAP、NSPostWhenIdle、和NSPostNow,这些风格将在接下来的部分中进行说明。
以NSPostASAP风格进入队列的通告会在运行循环的当前迭代完成时被发送给通告中心,如果当前运行循环模式和请求 的模式相匹配的话(如果请求的模式和当前模式不同,则通告在进入请求的模式时被发出)。由于运行循环在每个迭代过程中可能进行多个调用分支 (callout),所以在当前调用分支退出及控制权返回运行循环时,通告可能被分发,也可能不被分发。其它的调用分支可能先发生,比如定时器或由其它源 触发了事件,或者其它异步的通告被分发了。
您通常可以将NSPostASAP风格用于开销昂贵的资源,比如显示服务器。如果在运行循环的一个调用分支过程中有很多客户代码在窗口缓冲区中进行描画,在每次描画之后将缓冲区的内容刷新到显示服务器的开销是很昂贵的。在这种情况下,每个draw...方法都会将诸如“FlushTheServer” 这样的通告排入队列,并指定按名称和对象进行聚结,以及使用NSPostASAP风格。结果,在运行循环的最后,那些通告中只有一个被派发,而窗口缓冲区也只被刷新一次。
空闲时发送
以NSPostWhenIdle风格进入队列的通告只在运行循环处于等待状态时才被发出。在这种状态下,运行循环的输入通道中没有任何事件,包括定时器和异步事件。以NSPostWhenIdle风 格进入队列的一个典型的例子是当用户键入文本、而程序的其它地方需要显示文本字节长度的时候。在用户输入每一个字符后都对文本输入框的尺寸进行更新的开销
是很大的(而且不是特别有用),特别是当用户快速输入的时候。在这种情况下,Cocoa会在每个字符键入之后,将诸如 “ChangeTheDisplayedSize”这样的通告进行排队,同时把聚结开关打开,并使用NSPostWhenIdle风 格。当用户停止输入的时候,队列中只有一个“ChangeTheDisplayedSize”通告(由于聚结的原因)会在运行循环进入等待状态时被发出, 显示部分也因此被刷新。请注意,运行循环即将退出(当所有的输入通道都过时的时候,会发生这种情况)时并不处于等待状态,因此也不会发出通告。
以NSPostNow风格进入队列的通告会在聚结之后,立即发送到通告中心。您可以在不需要异步调用行为的时候 使用NSPostNow风格(或者通过NSNotificationCenter的postNotification:方法来发送)。在很多编程环境下,我们不仅允许同步的行为,而且希望使用这种行为:即您希望通告中心在通告派发之后返回,以便确定观察者对象收到通告并进行了处理。当然,当您希望通过聚结移除队列中类似的通告时,应该用enqueueNotification...方法,且使用NSPostNow风格,而不是使用postNotification:方法。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:40211次
排名:千里之外
原创:58篇
转载:20篇
(1)(5)(2)(29)(37)(1)(1)(2)在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知机制就可以告诉用户此时发生的事情。iOS中通知机制又叫消息机制,其包括两类:一类是本地通知;另一类是推送通知,也叫远程通知。两种通知在iOS中的表现一致,可以通过横幅或者弹出提醒两种形式告诉用户,并且点击通知可以会打开应用程序,但是实现原理却完全不同。今天就和大家一块去看一下如何在iOS中实现这两种机制,并且在文章后面会补充通知中心的内容避免初学者对两种概念的混淆。
本文包括下面内容
补充--iOS开发证书、秘钥
补充--通知中心
本地通知是由本地应用触发的,它是基于时间行为的一种通知形式,例如闹钟定时、待办事项提醒,又或者一个应用在一段时候后不使用通常会提示用户使用此应用等都是本地通知。创建一个本地通知通常分为以下几个步骤:
创建UILocalNotification。
设置处理通知的时间fireDate。
配置通知的内容:通知主体、通知声音、图标数字等。
配置通知传递的自定义数据参数userInfo(这一步可选)。
调用通知,可以使用scheduleLocalNotification:按计划调度一个通知,也可以使用presentLocalNotificationNow立即调用通知。
下面就以一个程序更新后用户长期没有使用的提醒为例对本地通知做一个简单的了解。在这个过程中并没有牵扯太多的界面操作,所有的逻辑都在AppDelegate中:进入应用后如果没有注册通知,需要首先注册通知请求用户允许通知;一旦调用完注册方法,无论用户是否选择允许通知此刻都会调用应用程序的- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings代理方法,在这个方法中根据用户的选择:如果是允许通知则会按照前面的步骤创建通知并在一定时间后执行。
AppDelegate.m
#import&&AppDelegate.h&
#import&&KCMainViewController.h&
@interface&AppDelegate&()
@implementation&AppDelegate
#pragma&mark&-&应用代理方法
-&(BOOL)application:(UIApplication&*)application&didFinishLaunchingWithOptions:(NSDictionary&*)launchOptions&{
&&&&_window=[[UIWindow&alloc]initWithFrame:[UIScreen&mainScreen].bounds];
&&&&_window.backgroundColor&=[UIColor&colorWithRed:249/255.0&green:249/255.0&blue:249/255.0&alpha:1];
&&&&[[UINavigationBar&appearance]&setBarTintColor:[UIColor&colorWithRed:23/255.0&green:180/255.0&blue:237/255.0&alpha:1]];
&&&&[[UINavigationBar&appearance]&setBarStyle:UIBarStyleBlack];
&&&&KCMainViewController&*mainController=[[KCMainViewController&alloc]init];
&&&&_window.rootViewController=mainC
&&&&[_window&makeKeyAndVisible];
&&&&if&([[UIApplication&sharedApplication]currentUserNotificationSettings].types!=UIUserNotificationTypeNone)&{
&&&&&&&&[self&addLocalNotification];
&&&&}else{
&&&&&&&&[[UIApplication&sharedApplication]registerUserNotificationSettings:[UIUserNotificationSettings&settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound&&categories:nil]];
&&&&return&YES;
#pragma&mark&调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication&*)application&didRegisterUserNotificationSettings:(UIUserNotificationSettings&*)notificationSettings{
&&&&if&(notificationSettings.types!=UIUserNotificationTypeNone)&{
&&&&&&&&[self&addLocalNotification];
#pragma&mark&进入前台后设置消息信息
-(void)applicationWillEnterForeground:(UIApplication&*)application{
&&&&[[UIApplication&sharedApplication]setApplicationIconBadgeNumber:0];
#pragma&mark&-&私有方法
#pragma&mark&添加本地通知
-(void)addLocalNotification{
&&&&UILocalNotification&*notification=[[UILocalNotification&alloc]init];
&&&&notification.fireDate=[NSDate&dateWithTimeIntervalSinceNow:10.0];
&&&&notification.repeatInterval=2;
&&&&notification.alertBody=@&最近添加了诸多有趣的特性,是否立即体验?&;&
&&&&notification.applicationIconBadgeNumber=1;
&&&&notification.alertAction=@&打开应用&;&
&&&&notification.alertLaunchImage=@&Default&;
&&&&notification.soundName=@&msg.caf&;
&&&&notification.userInfo=@{@&id&:@1,@&user&:@&Kenshin&Cui&};
&&&&[[UIApplication&sharedApplication]&scheduleLocalNotification:notification];
#pragma&mark&移除本地通知,在不需要此通知时记得移除
-(void)removeNotification{
&&&&[[UIApplication&sharedApplication]&cancelAllLocalNotifications];
请求获得用户允许通知的效果:
应用退出到后弹出通知的效果:
锁屏状态下的通知效果(从这个界面可以看到alertAction配置为“打开应用”):
在使用通知之前必须注册通知类型,如果用户不允许应用程序发送通知,则以后就无法发送通知,除非用户手动到iOS设置中打开通知。
本地通知是有操作系统统一调度的,只有在应用退出到后台或者关闭才能收到通知。(注意:这一点对于后面的推送通知也是完全适用的。 )
通知的声音是由iOS系统播放的,格式必须是Linear PCM、MA4(IMA/ADPCM)、μLaw、aLaw中的一种,并且播放时间必须在30s内,否则将被系统声音替换,同时自定义声音文件必须放到main boundle中。
本地通知的数量是有限制的,最近的本地通知最多只能有64个,超过这个数量将被系统忽略。
如果想要移除本地通知可以调用UIApplication的cancelLocalNotification:或cancelAllLocalNotifications移除指定通知或所有通知。
从上面的程序可以看到userInfo这个属性我们设置了参数,那么这个参数如何接收呢?
在iOS中如果点击一个弹出通知(或者锁屏界面滑动查看通知),默认会自动打开当前应用。由于通知由系统调度那么此时进入应用有两种情况:如果应用程序已经完全退出那么此时会调用-&(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法;如果此时应用程序还在运行(无论是在前台还是在后台)则会调用-(void)application:(UIApplication
*)application didReceiveLocalNotification:(UILocalNotification *)notification方法接收消息参数。当然如果是后者自然不必多说,因为参数中已经可以拿到notification对象,只要读取userInfo属性即可。如果是前者的话则可以访问launchOptions中键为UIApplicationLaunchOptionsLocalNotificationKey的对象,这个对象就是发送的通知,由此对象再去访问userInfo。为了演示这个过程在下面的程序中将userInfo的内容写入文件以便模拟关闭程序后再通过点击通知打开应用获取userInfo的过程。
AppDelegate.m
#import&&AppDelegate.h&
#import&&KCMainViewController.h&
@interface&AppDelegate&()
@implementation&AppDelegate
#pragma&mark&-&应用代理方法
-&(BOOL)application:(UIApplication&*)application&didFinishLaunchingWithOptions:(NSDictionary&*)launchOptions&{
&&&&_window=[[UIWindow&alloc]initWithFrame:[UIScreen&mainScreen].bounds];
&&&&_window.backgroundColor&=[UIColor&colorWithRed:249/255.0&green:249/255.0&blue:249/255.0&alpha:1];
&&&&[[UINavigationBar&appearance]&setBarTintColor:[UIColor&colorWithRed:23/255.0&green:180/255.0&blue:237/255.0&alpha:1]];
&&&&[[UINavigationBar&appearance]&setBarStyle:UIBarStyleBlack];
&&&&KCMainViewController&*mainController=[[KCMainViewController&alloc]init];
&&&&_window.rootViewController=mainC
&&&&[_window&makeKeyAndVisible];
&&&&[self&addLocalNotification];
&&&&UILocalNotification&*notification=[launchOptions&valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
&&&&NSDictionary&*userInfo=&notification.userI
&&&&[userInfo&writeToFile:@&/Users/kenshincui/Desktop/didFinishLaunchingWithOptions.txt&&atomically:YES];
&&&&NSLog(@&didFinishLaunchingWithOptions:The&userInfo&is&%@.&,userInfo);
&&&&return&YES;
#pragma&mark&接收本地通知时触发
-(void)application:(UIApplication&*)application&didReceiveLocalNotification:(UILocalNotification&*)notification{
&&&&NSDictionary&*userInfo=notification.userI
&&&&[userInfo&writeToFile:@&/Users/kenshincui/Desktop/didReceiveLocalNotification.txt&&atomically:YES];
&&&&NSLog(@&didReceiveLocalNotification:The&userInfo&is&%@&,userInfo);
#pragma&mark&调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication&*)application&didRegisterUserNotificationSettings:(UIUserNotificationSettings&*)notificationSettings{
&&&&if&(notificationSettings.types!=UIUserNotificationTypeNone)&{
&&&&&&&&[self&addLocalNotification];
#pragma&mark&进入前台后设置消息信息
-(void)applicationWillEnterForeground:(UIApplication&*)application{
&&&&[[UIApplication&sharedApplication]setApplicationIconBadgeNumber:0];
#pragma&mark&-&私有方法
#pragma&mark&添加本地通知
-(void)addLocalNotification{
&&&&UILocalNotification&*notification=[[UILocalNotification&alloc]init];
&&&&notification.fireDate=[NSDate&dateWithTimeIntervalSinceNow:10.0];
&&&&notification.repeatInterval=2;
&&&&notification.alertBody=@&最近添加了诸多有趣的特性,是否立即体验?&;&
&&&&notification.applicationIconBadgeNumber=1;
&&&&notification.alertAction=@&打开应用&;&
&&&&notification.alertLaunchImage=@&Default&;
&&&&notification.soundName=@&msg.caf&;
&&&&notification.userInfo=@{@&id&:@1,@&user&:@&Kenshin&Cui&};
&&&&[[UIApplication&sharedApplication]&scheduleLocalNotification:notification];
上面的程序可以分为两种情况去运行:一种是启动程序关闭程序,等到接收到通知之后点击通知重新进入程序;另一种是启动程序后,进入后台(其实在前台也可以,但是为了明显的体验这个过程建议进入后台),接收到通知后点击通知进入应用。另种情况会分别按照前面说的情况调用不同的方法接收到userInfo写入本地文件系统。有了userInfo一般来说就可以根据这个信息进行一些处理,例如可以根据不同的参数信息导航到不同的界面,假设是更新的通知则可以导航到更新内容界面等。
和本地通知不同,推送通知是由应用服务提供商发起的,通过苹果的APNs(Apple Push Notification Server)发送到应用客户端。下面是苹果官方关于推送通知的过程示意图:
推送通知的过程可以分为以下几步:
应用服务提供商从服务器端把要发送的消息和设备令牌(device token)发送给苹果的消息推送服务器APNs。
APNs根据设备令牌在已注册的设备(iPhone、iPad、iTouch、mac等)查找对应的设备,将消息发送给相应的设备。
客户端设备接将接收到的消息传递给相应的应用程序,应用程序根据用户设置弹出通知消息。
当然,这只是一个简单的流程,有了这个流程我们还无从下手编写程序,将上面的流程细化可以得到如下流程图(图片来自互联网),在这个过程中会也会提到如何在程序中完成这些步骤:
1.应用程序注册APNs推送消息
a.只有注册过的应用才有可能接收到消息,程序中通常通过UIApplication的registerUserNotificationSettings:方法注册,iOS8中通知注册的方法发生了改变,如果是iOS7及之前版本的iOS请参考其他代码。
b.注册之前有两个前提条件必须准备好:开发配置文件(provisioning profile,也就是.mobileprovision后缀的文件)的App ID不能使用通配ID必须使用指定APP ID并且生成配置文件中选择Push Notifications服务,一般的开发配置文件无法完成注册;应用程序的Bundle Identifier必须和生成配置文件使用的APP ID完全一致。
2.iOS从APNs接收device token,在应用程序获取device token
a.在UIApplication的-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken代理方法中获取令牌,此方法发生在注册之后。
b.如果无法正确获得device token可以在UIApplication的-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error代理方法中查看详细错误信息,此方法发生在获取device token失败之后。
c.必须真机调试,模拟器无法获取device token。
3.iOS应用将device token发送给应用程序提供商,告诉服务器端当前设备允许接收消息
a.device token的生成算法只有Apple掌握,为了确保算法发生变化后仍然能够正常接收服务器端发送的通知,每次应用程序启动都重新获得device token(注意:device token的获取不会造成性能问题,苹果官方已经做过优化)。
b.通常可以创建一个网络连接发送给应用程序提供商的服务器端, 在这个过程中最好将上一次获得的device token存储起来,避免重复发送,一旦发现device token发生了变化最好将原有的device token一块发送给服务器端,服务器端删除原有令牌存储新令牌避免服务器端发送无效消息。
4.应用程序提供商在服务器端根据前面发送过来的device token组织信息发送给APNs
a.发送时指定device token和消息内容,并且完全按照苹果官方的消息格式组织消息内容,通常情况下可以借助其他第三方消息推送框架来完成。
5.APNs根据消息中的device token查找已注册的设备推送消息
a.正常情况下可以根据device token将消息成功推送到客户端设备中,但是也不排除用户卸载程序的情况,此时推送消息失败,APNs会将这个错误消息通知服务器端以避免资源浪费(服务器端此时可以根据错误删除已经存储的device token,下次不再发送)。
下面将简单演示一下推送通知的简单流程:
首先,看一下iOS客户端代码:
#import&&AppDelegate.h&
#import&&KCMainViewController.h&
@interface&AppDelegate&()
@implementation&AppDelegate
#pragma&mark&-&应用程序代理方法
#pragma&mark&应用程序启动之后
-&(BOOL)application:(UIApplication&*)application&didFinishLaunchingWithOptions:(NSDictionary&*)launchOptions&{
&&&&_window=[[UIWindow&alloc]initWithFrame:[UIScreen&mainScreen].bounds];
&&&&_window.backgroundColor&=[UIColor&colorWithRed:249/255.0&green:249/255.0&blue:249/255.0&alpha:1];
&&&&[[UINavigationBar&appearance]&setBarTintColor:[UIColor&colorWithRed:23/255.0&green:180/255.0&blue:237/255.0&alpha:1]];
&&&&[[UINavigationBar&appearance]&setBarStyle:UIBarStyleBlack];
&&&&KCMainViewController&*mainController=[[KCMainViewController&alloc]init];
&&&&_window.rootViewController=mainC
&&&&[_window&makeKeyAndVisible];
&&&&[application&registerUserNotificationSettings:[UIUserNotificationSettings&settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound&categories:nil]];
&&&&[application&registerForRemoteNotifications];
&&&&return&YES;
#pragma&mark&注册推送通知之后
-(void)application:(UIApplication&*)application&didRegisterForRemoteNotificationsWithDeviceToken:(NSData&*)deviceToken{
&&&&[self&addDeviceToken:deviceToken];
&&&&NSLog(@&device&token:%@&,deviceToken);
#pragma&mark&获取device&token失败后
-(void)application:(UIApplication&*)application&didFailToRegisterForRemoteNotificationsWithError:(NSError&*)error{
&&&&NSLog(@&didFailToRegisterForRemoteNotificationsWithError:%@&,error.localizedDescription);
&&&&[self&addDeviceToken:nil];
#pragma&mark&接收到推送通知之后
-(void)application:(UIApplication&*)application&didReceiveRemoteNotification:(NSDictionary&*)userInfo{
&&&&NSLog(@&receiveRemoteNotification,userInfo&is&%@&,userInfo);
#pragma&mark&-&私有方法
-(void)addDeviceToken:(NSData&*)deviceToken{
&&&&NSString&*key=@&DeviceToken&;
&&&&NSData&*oldToken=&[[NSUserDefaults&standardUserDefaults]objectForKey:key];
&&&&if&(![oldToken&isEqualToData:deviceToken])&{
&&&&&&&&[[NSUserDefaults&standardUserDefaults]&setObject:deviceToken&forKey:key];
&&&&&&&&[self&sendDeviceTokenWidthOldDeviceToken:oldToken&newDeviceToken:deviceToken];
-(void)sendDeviceTokenWidthOldDeviceToken:(NSData&*)oldToken&newDeviceToken:(NSData&*)newToken{
&&&&NSString&*urlStr=@&&;
&&&&urlStr=[urlStr&stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
&&&&NSURL&*url=[NSURL&URLWithString:urlStr];
&&&&NSMutableURLRequest&*requestM=[NSMutableURLRequest&requestWithURL:url&cachePolicy:0&timeoutInterval:10.0];
&&&&[requestM&setHTTPMethod:@&POST&];
&&&&NSString&*bodyStr=[NSString&stringWithFormat:@&oldToken=%@&newToken=%@&,oldToken,newToken];
&&&&NSData&*body=[bodyStr&dataUsingEncoding:NSUTF8StringEncoding];
&&&&[requestM&setHTTPBody:body];
&&&&NSURLSession&*session=[NSURLSession&sharedSession];
&&&&NSURLSessionDataTask&*dataTask=&[session&dataTaskWithRequest:requestM&completionHandler:^(NSData&*data,&NSURLResponse&*response,&NSError&*error)&{
&&&&&&&&if&(error)&{
&&&&&&&&&&&&NSLog(@&Send&failure,error&is&:%@&,error.localizedDescription);
&&&&&&&&}else{
&&&&&&&&&&&&NSLog(@&Send&Success!&);
&&&&[dataTask&resume];
iOS客户端代码的代码比较简单,注册推送通知,获取device token存储到偏好设置中,并且如果新获取的device token不同于偏好设置中存储的数据则发送给服务器端,更新服务器端device token列表。
其次,由于device token需要发送给服务器端,这里使用一个Web应用作为服务器端接收device token,这里使用了ASP.NET程序来处理令牌接收注册工作,当然你使用其他技术同样没有问题。下面是对应的后台代码:
using&System.Collections.G
using&System.W
using&System.Web.UI;
using&System.Web.UI.WebC
using&CMJ.Framework.D
namespace&WebServer
&&&&public&partial&class&RegisterDeviceToken&:&System.Web.UI.Page
&&&&&&&&private&string&_appID&=&@&com.cmjstudio.pushnotification&;
&&&&&&&&private&SqlHelper&_helper&=&new&SqlHelper();
&&&&&&&&protected&void&Page_Load(object&sender,&EventArgs&e)
&&&&&&&&&&&&try
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&string&oldToken&=&Request[&oldToken&]&+&&&;
&&&&&&&&&&&&&&&&string&newToken&=&Request[&newToken&]&+&&&;
&&&&&&&&&&&&&&&&string&sql&=&&&;
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&if&(oldToken&!=&&&)
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&sql&=&string.Format(&DELETE&FROM&dbo.Device&WHERE&AppID='{0}'&AND&DeviceToken='{1}';&,&_appID,&oldToken);
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&sql&+=&string.Format(@&IF&NOT&EXISTS&(SELECT&ID&FROM&dbo.Device&WHERE&AppID='{0}'&AND&DeviceToken='{1}')
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&INSERT&INTO&dbo.Device&(&AppID,&DeviceToken&)&VALUES&(&N'{0}',&N'{1}');&,&_appID,&newToken);
&&&&&&&&&&&&&&&&_helper.ExecuteNonQuery(sql);
&&&&&&&&&&&&&&&&Response.Write(&注册成功!&);
&&&&&&&&&&&&}
&&&&&&&&&&&&catch(Exception&ex)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&Response.Write(&注册失败,错误详情:&+ex.ToString());
&&&&&&&&&&&&}
这个过程主要就是保存device token到数据库中,当然如果同时传递旧的设备令牌还需要先删除就的设备令牌,这里简单的在数据库中创建了一张Device表来保存设备令牌,其中记录了应用程序Id和设备令牌。
第三步就是服务器端发送消息,如果要给APNs发送消息就必须按照Apple的标准消息格式组织消息内容。但是好在目前已经有很多开源的第三方类库供我们使用,具体消息如何包装完全不用自己组织,这里使用一个开源的类库来给APNs发送消息 ,除了可以给Apple设备推送消息,Push Sharp还支持Android、Windows Phone等多种设备,更多详细内容大家可以参照官方说明。前面说过如果要开发消息推送应用不能使用一般的开发配置文件,这里还需要注意:如果服务器端要给APNs发送消息其秘钥也必须是通过APNs Development iOS类型的证书来导出的,一般的iOS Development 类型的证书导出的秘钥无法用作服务器端发送秘钥。下面通过在一个简单的WinForm程序中调用Push
Sharp给APNs发送消息,这里读取之前Device表中的所有设备令牌循环发送消息:
using&System.IO;
using&System.Collections.G
using&ponentM
using&System.D
using&System.D
using&System.T
using&System.Windows.F
using&PushS
using&PushSharp.A
using&CMJ.Framework.D
using&CMJ.Framework.L
using&CMJ.Framework.Windows.F
namespace&PushNotificationServer
&&&&public&partial&class&frmMain&:&PersonalizeForm
&&&&&&&&private&string&_appID&=&@&com.cmjstudio.pushnotification&;
&&&&&&&&private&SqlHelper&_helper&=&new&SqlHelper();
&&&&&&&&public&frmMain()
&&&&&&&&&&&&InitializeComponent();
&&&&&&&&private&void&btnClose_Click(object&sender,&EventArgs&e)
&&&&&&&&&&&&this.Close();
&&&&&&&&private&void&btnSend_Click(object&sender,&EventArgs&e)
&&&&&&&&&&&&List&deviceTokens&=&GetDeviceToken();
&&&&&&&&&&&&SendMessage(deviceTokens,&tbMessage.Text);
&&&&&&&&#region&发送消息
&&&&&&&&&&&&List&deviceTokens&=&new&List();
&&&&&&&&&&&&string&sql&=&string.Format(&SELECT&DeviceToken&FROM&dbo.Device&WHERE&AppID='{0}'&,_appID);
&&&&&&&&&&&&DataTable&dt&=&_helper.GetDataTable(sql);
&&&&&&&&&&&&if(dt.Rows.Count&0)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&foreach(DataRow&dr&in&dt.Rows)
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&deviceTokens.Add((dr[&DeviceToken&]+&&).TrimStart('').Replace(&&&,&&));
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&&&&&return&deviceT
&&&&&&&&&&&&
&&&&&&&&&&&&var&pusher&=&new&PushBroker();
&&&&&&&&&&&&pusher.OnNotificationSent&+=&pusher_OnNotificationS
&&&&&&&&&&&&pusher.OnNotificationFailed&+=&pusher_OnNotificationF
&&&&&&&&&&&&pusher.OnChannelCreated&+=&pusher_OnChannelC
&&&&&&&&&&&&pusher.OnChannelDestroyed&+=&pusher_OnChannelD
&&&&&&&&&&&&pusher.OnChannelException&+=&pusher_OnChannelE
&&&&&&&&&&&&pusher.OnDeviceSubscriptionChanged&+=&pusher_OnDeviceSubscriptionC
&&&&&&&&&&&&pusher.OnDeviceSubscriptionExpired&+=&pusher_OnDeviceSubscriptionE
&&&&&&&&&&&&pusher.OnNotificationRequeue&+=&pusher_OnNotificationR
&&&&&&&&&&&&pusher.OnServiceException&+=&pusher_OnServiceE
&&&&&&&&&&&&
&&&&&&&&&&&&byte[]&certificateData&=&File.ReadAllBytes(@&E:\KenshinCui_Push.p12&);
&&&&&&&&&&&&pusher.RegisterAppleService(new&ApplePushChannelSettings(certificateData,&&123&));
&&&&&&&&&&&&foreach&(string&token&in&deviceToken)
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&pusher.QueueNotification(new&AppleNotification()
&&&&&&&&&&&&&&&&&&&&.ForDeviceToken(token)
&&&&&&&&&&&&&&&&&&&&.WithAlert(message)&
&&&&&&&&&&&&&&&&&&&&.WithBadge(1)
&&&&&&&&&&&&&&&&&&&&.WithSound(&default&));
&&&&&&&&&&&&}
&&&&&&&&void&pusher_OnServiceException(object&sender,&Exception&error)
&&&&&&&&&&&&Console.WriteLine(&消息发送失败,错误详情:&&+&error.ToString());
&&&&&&&&&&&&PersonalizeMessageBox.Show(this,&&消息发送失败,错误详情:&&+&error.ToString(),&&系统提示&);
&&&&&&&&void&pusher_OnNotificationRequeue(object&sender,&PushSharp.Core.NotificationRequeueEventArgs&e)
&&&&&&&&&&&&Console.WriteLine(&pusher_OnNotificationRequeue&);
&&&&&&&&void&pusher_OnDeviceSubscriptionExpired(object&sender,&string&expiredSubscriptionId,&DateTime&expirationDateUtc,&PushSharp.Core.INotification&notification)
&&&&&&&&&&&&Console.WriteLine(&pusher_OnDeviceSubscriptionChanged&);
&&&&&&&&void&pusher_OnDeviceSubscriptionChanged(object&sender,&string&oldSubscriptionId,&string&newSubscriptionId,&PushSharp.Core.INotification&notification)
&&&&&&&&&&&&Console.WriteLine(&pusher_OnDeviceSubscriptionChanged&);
&&&&&&&&void&pusher_OnChannelException(object&sender,&PushSharp.Core.IPushChannel&pushChannel,&Exception&error)
&&&&&&&&&&&&Console.WriteLine(&消息发送失败,错误详情:&&+&error.ToString());
&&&&&&&&&&&&PersonalizeMessageBox.Show(this,&&消息发送失败,错误详情:&&+&error.ToString(),&&系统提示&);
&&&&&&&&void&pusher_OnChannelDestroyed(object&sender)
&&&&&&&&&&&&Console.WriteLine(&pusher_OnChannelDestroyed&);
&&&&&&&&void&pusher_OnChannelCreated(object&sender,&PushSharp.Core.IPushChannel&pushChannel)
&&&&&&&&&&&&Console.WriteLine(&pusher_OnChannelCreated&);
&&&&&&&&void&pusher_OnNotificationFailed(object&sender,&PushSharp.Core.INotification&notification,&Exception&error)
&&&&&&&&&&&&Console.WriteLine(&消息发送失败,错误详情:&&+&error.ToString());
&&&&&&&&&&&&PersonalizeMessageBox.Show(this,&&消息发送失败,错误详情:&+error.ToString(),&&系统提示&);
&&&&&&&&void&pusher_OnNotificationSent(object&sender,&PushSharp.Core.INotification&notification)
&&&&&&&&&&&&Console.WriteLine(&消息发送成功!&);
&&&&&&&&&&&&PersonalizeMessageBox.Show(this,&&消息发送成功!&,&&系统提示&);
&&&&&&&&#endregion
服务器端消息发送应用运行效果:
iOS客户端接收的消息的效果:
到目前为止通过服务器端应用可以顺利发送消息给APNs并且iOS应用已经成功接收推送消息。
补充--iOS开发证书、秘钥
iOS开发过程中如果需要进行真机调试、发布需要注册申请很多证书,对于初学者往往迷惑不解,再加上今天的文章中会牵扯到一些特殊配置,这里就简单的对iOS开发的常用证书和秘钥等做一说明。
iOS常用的证书包括开发证书和发布证书,无论是真机调试还是最终发布应用到App Store这两个证书都是必须的,它是iOS开发的基本证书。
a.开发证书:开发证书又分为普通开发证书和推送证书,如果仅仅是一般的应用则前者即可满足,但是如果开发推送应用则必须使用推送证书。
b.发布证书:发布证书又可以分为普通发布证书、推送证书、Pass Type ID证书、站点发布证书、VoIP服务证书、苹果支付证书。同样的,对于需要使用特殊服务的应用则必须选择对应的证书。
App ID,应用程序的唯一标识,对应iOS应用的Bundle Identifier,App ID在苹果开发者中心中分为通配应用ID和明确的应用ID,前者一般用于普通应用开发,一个ID可以适用于多个不同标识的应用;但是对于使用消息推送、Passbook、站点发布、iCloud等服务的应用必须配置明确的应用ID。
UDID,用于标识每一台硬件设备的标示符。注意它不是device token,device token是根据UDID使用一个只有Apple自己才知道的算法生成的一组标示符。
Provisioning Profiles,平时又称为PP文件。将UDID、App ID、开发证书打包在一起的配置文件,同样分为开发和发布两类配置文件。
在申请开发证书时必须要首先提交一个秘钥请求文件,对于生成秘钥请求文件的mac,如果要做开发则只需要下载证书和配置简介即可开发。但是如果要想在其他机器上做开发则必须将证书中的秘钥导出(导出之后是一个.p12文件),然后导入其他机器。同时对于类似于推送服务器端应用如果要给APNs发送消息,同样需要使用.p12秘钥文件,并且这个秘钥文件需要是推送证书导出的对应秘钥。
补充--通知中心
对于很多初学者往往会把iOS中的本地通知、推送通知和iOS通知中心的概念弄混。其实二者之间并没有任何关系,事实上它们都不属于一个框架,前者属于UIKit框架,后者属于Foundation框架。
通知中心实际上是iOS程序内部之间的一种消息广播机制,主要为了解决应用程序内部不同对象之间解耦而设计。它是基于观察者模式设计的,不能跨应用程序进程通信,当通知中心接收到消息之后会根据内部的消息转发表,将消息发送给订阅者。下面是一个简单的流程示意图:
了解通知中心需要熟悉NSNotificationCenter和NSNotification两个类:
NSNotificationCenter:是通知系统的中心,用于注册和发送通知,下表列出常用的方法。
NSNotification:代表通知内容的载体,主要有三个属性:name代表通知名称,object代表通知的发送者,userInfo代表通知的附加信息。
虽然前面的文章中从未提到过通知中心,但是其实通知中心我们并不陌生,前面文章中很多内容都是通过通知中心来进行应用中各个组件通信的,只是没有单独拿出来说而已。例如前面的文章中讨论的应用程序生命周期问题,当应用程序启动后、进入后台、进入前台、获得焦点、失去焦点,窗口大小改变、隐藏等都会发送通知。这个通知可以通过前面NSNotificationCenter进行订阅即可接收对应的消息,下面的示例演示了如何添加监听获得UIApplication的进入后台和获得焦点的通知:
#import&&KCMainViewController.h&
@interface&KCMainViewController&()
@implementation&KCMainViewController
-&(void)viewDidLoad&{
&&&&[super&viewDidLoad];
&&&&[self&addObserverToNotificationCenter];
#pragma&mark&添加监听
-(void)addObserverToNotificationCenter{
&&&&[[NSNotificationCenter&defaultCenter]&addObserver:self&selector:@selector(applicationEnterBackground)&name:UIApplicationDidEnterBackgroundNotification&object:[UIApplication&sharedApplication]];
&&&&NSOperationQueue&*operationQueue=[[NSOperationQueue&alloc]init];
&&&&[[NSNotificationCenter&defaultCenter]&addObserverForName:UIApplicationDidBecomeActiveNotification&object:[UIApplication&sharedApplication]&queue:operationQueue&usingBlock:^(NSNotification&*note)&{
&&&&&&&&NSLog(@&Application&become&active.&);
#pragma&mark&应用程序启动监听方法
-(void)applicationEnterBackground{
&&&&NSLog(@&Application&enter&background.&);
当然很多时候使用通知中心是为了添加自定义通知,并获得自定义通知消息。在前面的文章“”中提到过如何进行多视图之间参数传递,其实利用自定义通知也可以进行参数传递。通常一个应用登录后会显示用户信息,而登录信息可以通过登录界面获取。下面就以这样一种场景为例,在主界面中添加监听,在登录界面发送通知,一旦登录成功将向通知中心发送成功登录的通知,此时主界面中由于已经添加通知监听所以会收到通知并更新UI界面。
主界面KCMainViewController.m:
#import&&KCMainViewController.h&
#import&&KCLoginViewController.h&
#define&UPDATE_LGOGIN_INFO_NOTIFICATION&@&updateLoginInfo&
@interface&KCMainViewController&(){
&&&&UILabel&*_lbLoginI
&&&&UIButton&*_btnL
@implementation&KCMainViewController
-&(void)viewDidLoad&{
&&&&[super&viewDidLoad];
&&&&[self&setupUI];
-(void)setupUI{
&&&&UILabel&*label&=[[UILabel&alloc]initWithFrame:CGRectMake(0,&100,320&,30)];
&&&&label.textAlignment=NSTextAlignmentC
&&&&label.textColor=[UIColor&colorWithRed:23/255.0&green:180/255.0&blue:237/255.0&alpha:1];
&&&&_lbLoginInfo=
&&&&[self.view&addSubview:label];
&&&&UIButton&*button=[UIButton&buttonWithType:UIButtonTypeSystem];
&&&&button.frame=CGRectMake(60,&200,&200,&25);
&&&&[button&setTitle:@&登录&&forState:UIControlStateNormal];
&&&&[button&addTarget:self&action:@selector(loginOut)&forControlEvents:UIControlEventTouchUpInside];
&&&&_btnLogin=
&&&&[self.view&addSubview:button];
-(void)loginOut{
&&&&[self&addObserverToNotification];
&&&&KCLoginViewController&*loginController=[[KCLoginViewController&alloc]init];
&&&&[self&presentViewController:loginController&animated:YES&completion:nil];
-(void)addObserverToNotification{
&&&&[[NSNotificationCenter&defaultCenter]&addObserver:self&selector:@selector(updateLoginInfo:)&name:UPDATE_LGOGIN_INFO_NOTIFICATION&object:nil];
-(void)updateLoginInfo:(NSNotification&*)notification{
&&&&NSDictionary&*userInfo=notification.userI
&&&&_lbLoginInfo.text=userInfo[@&loginInfo&];
&&&&_btnLogin.titleLabel.text=@&注销&;
-(void)dealloc{
&&&&[[NSNotificationCenter&defaultCenter]&removeObserver:self];
登录界面KCLoginViewController.m:
#import&&KCLoginViewController.h&
#define&UPDATE_LGOGIN_INFO_NOTIFICATION&@&updateLoginInfo&
@interface&KCLoginViewController&(){
&&&&UITextField&*_txtUserN
&&&&UITextField&*_txtP
@implementation&KCLoginViewController
-&(void)viewDidLoad&{
&&&&[super&viewDidLoad];
&&&&[self&setupUI];
-(void)setupUI{
&&&&UILabel&*lbUserName=[[UILabel&alloc]initWithFrame:CGRectMake(50,&150,&100,&30)];
&&&&lbUserName.text=@&用户名:&;
&&&&[self.view&addSubview:lbUserName];
&&&&_txtUserName=[[UITextField&alloc]initWithFrame:CGRectMake(120,&150,&150,&30)];
&&&&_txtUserName.borderStyle=UITextBorderStyleRoundedR
&&&&[self.view&addSubview:_txtUserName];
&&&&UILabel&*lbPassword=[[UILabel&alloc]initWithFrame:CGRectMake(50,&200,&100,&30)];
&&&&lbPassword.text=@&密码:&;
&&&&[self.view&addSubview:lbPassword];
&&&&_txtPassword=[[UITextField&alloc]initWithFrame:CGRectMake(120,&200,&150,&30)];
&&&&_txtPassword.secureTextEntry=YES;
&&&&_txtPassword.borderStyle=UITextBorderStyleRoundedR
&&&&[self.view&addSubview:_txtPassword];
&&&&UIButton&*btnLogin=[UIButton&buttonWithType:UIButtonTypeSystem];
&&&&btnLogin.frame=CGRectMake(70,&270,&80,&30);
&&&&[btnLogin&setTitle:@&登录&&forState:UIControlStateNormal];
&&&&[self.view&addSubview:btnLogin];
&&&&[btnLogin&addTarget:self&action:@selector(login)&forControlEvents:UIControlEventTouchUpInside];
&&&&UIButton&*btnCancel=[UIButton&buttonWithType:UIButtonTypeSystem];
&&&&btnCancel.frame=CGRectMake(170,&270,&80,&30);
&&&&[btnCancel&setTitle:@&取消&&forState:UIControlStateNormal];
&&&&[self.view&addSubview:btnCancel];
&&&&[btnCancel&addTarget:self&action:@selector(cancel)&forControlEvents:UIControlEventTouchUpInside];
#pragma&mark&登录操作
-(void)login{
&&&&if&([_txtUserName.text&isEqualToString:@&kenshincui&]&&&&[_txtPassword.text&isEqualToString:@&123&]&)&{
&&&&&&&&[self&postNotification];
&&&&&&&&[self&dismissViewControllerAnimated:YES&completion:nil];
&&&&}else{
&&&&&&&&UIAlertView&*alertView=[[UIAlertView&alloc]initWithTitle:@&系统信息&&message:@&用户名或密码错误,请重新输入!&&delegate:nil&cancelButtonTitle:@&取消&&otherButtonTitles:nil];
&&&&&&&&[alertView&show];
#pragma&mark&点击取消
-(void)cancel{
&&&&[self&dismissViewControllerAnimated:YES&completion:nil];
-(void)postNotification{
&&&&NSDictionary&*userInfo=@{@&loginInfo&:[NSString&stringWithFormat:@&Hello,%@!&,_txtUserName.text]};
&&&&NSLog(@&%@&,userInfo);
&&&&NSNotification&*notification=[NSNotification&notificationWithName:UPDATE_LGOGIN_INFO_NOTIFICATION&object:self&userInfo:userInfo];
&&&&[[NSNotificationCenter&defaultCenter]&postNotification:notification];
运行效果:
通过上面的介绍大家应该可以发现其实通知中心是一种低耦合设计,和前面文章中提到的代理模式有异曲同工之妙。相对于后者而言,通知中心可以将一个通知发送给多个监听者,而每个对象的代理却只能有一个。当然代理也有其优点,例如使用代理代码分布结构更加清晰,它不像通知一样随处都可以添加订阅等,实际使用过程中需要根据实际情况而定。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:6909次
排名:千里之外
转载:36篇}

我要回帖

更多关于 微信支付退款发起通知 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信