推送通知
推送通知跟NSNotification有所区别: 
1> NSNotification是抽象的,不可见的 
2> 推送通知是可见的(能用肉眼看到)
iOS中提供了2种推送通知: 本地推送通知, 远程推送通知 
1> 本地推送通知(Local Notification) 
2> 远程推送通知(Remote Notification)
推送通知可以不让在前台运行的app,告知app内部发生了什么变化,比如:有新的内容,新消息等.
推送通知的使用: 
发出推送通知时,如果当前程序正运行在前台,那么推送通知就不会被呈现出来 
点击推送通知后,默认会自动打开发出推送通知的app 
不管app打开还是关闭,推送通知都能如期发出
推送通知有5种呈现效果: 
1. 在屏幕顶部显示一块横幅(显示具体内容) 
2. 在屏幕中间弹出一个UIAlertView(显示具体内容,使用较少) 
3. 在锁屏界面显示一块横幅(锁屏状态下,显示具体内容) 
4. 播放音效(提醒作用) 
5. 更新app图标的数字(说明新内容的数量)
推送通知分为本地推送通知和远程推送通知,下面一一介绍.
本地推送通知
本地推送通知不需要服务器的支持,不需要联网就可以发送通知.通常本地推送通知用于定时提醒用户,比如清理垃圾,淘宝购物,纪念日提醒等任务. 
在苹果官方给出了本地推送通知的一些属性,以及使用.
属性介绍:
  @property(nonatomic,copy) NSDate *fireDate; // 设置本地推送的时间
  @property(nonatomic,copy) NSTimeZone *timeZone; // 时区(一般设置为[NSTimeZone defaultTimeZone] ,跟随手机的时区)
  @property(nonatomic) NSCalendarUnit repeatInterval; // 没隔多久重复发出一次
  @property(nonatomic,copy) NSCalendar *repeatCalendar; // 设置日期
  @property(nonatomic,copy) CLRegion *region NS_AVAILABLE_IOS(8_0); // 比如某一个区域的时候发出通知
  @property(nonatomic,assign) BOOL regionTriggersOnce NS_AVAILABLE_IOS(8_0); // 进入区域是否重复
  @property(nonatomic,copy) NSDictionary *userInfo;  // 附加的额外信息
  @property(nonatomic,copy) NSString *alertBody; // 消息的内容
  @property(nonatomic) BOOL hasAction; // 是否显示alertAction的文字(默认是YES)
  @property(nonatomic,copy) NSString *alertAction;  // 设置锁屏状态下,显示的一个文字
  @property(nonatomic,copy) NSString *alertLaunchImage; // 启动图片
  @property(nonatomic,copy) NSString *soundName;   //  UILocalNotificationDefaultSoundName
  @property(nonatomic) NSInteger applicationIconBadgeNumber; // 应用图标右上角的提醒数字
  @property(nonatomic,copy) NSArray *scheduledLocalNotifications; // 获得被调度(定制)的所有本地推送通知(已经发出且过期的推送通知就算调度结束,会自动从这个数组中移除)
使用方法:
创建本地通知
UILocalNotification *localNoti = [[UILocalNotification alloc] init];
调度本地推送通知(调度完毕后,推送通知会在特地时间fireDate发出)
[[UIApplication sharedApplication] scheduleLocalNotification:localNoti];
取消调度本地推送通知
- (void)cancelLocalNotification:(UILocalNotification *)notification;
- (void)cancelAllLocalNotifications;
- (void)presentLocalNotificationNow:(UILocalNotification *)notification;
注意: 
iOS8.0以后在本地推送通知上新加了一些新功能,为了用户体验,以及更加人性化,如果要使用本地通知,需要得到用户的许可. 
需要在AppDelegate中添加如下代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    /*
     UIUserNotificationTypeNone    = 0,      没有,没有本地通知
     UIUserNotificationTypeBadge   = 1 << 0, 接受图标右上角提醒数字
     UIUserNotificationTypeSound   = 1 << 1, 接受通知时候,可以发出音效
     UIUserNotificationTypeAlert   = 1 << 2, 接受提醒(横幅/弹窗)
     */
    // iOS8需要添加请求用户的授权
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
        [application registerUserNotificationSettings:settings];
    }
}
点击本地推送通知 
当用户点击本地推送通知,会自动打开app,这里有2种情况
1> app并没有关闭,一直隐藏在后台(运行在后台) 
让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app)
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
2> app已经被关闭(进程已死) 
启动app,启动完毕会调用AppDelegate的下面方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象
若要实现界面的跳转,需要分清当前应用程序的所处状态,进行判断: 
1> 若是当前应用在后台运行,接收到通知时,要想进行界面的跳转,可以在didReceiveLocalNotification:方法中实现跳转界面的方法 
2> 若是当前的应用程序已经关闭,我们在前面说到,当应用关闭,推送通知也会如期发送.但此时,是不会走didReceiveLocalNotification:方法的,那我们只有didFinishLaunchingWithOptions:方法利用,launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象,实现跳转的功能.
下面是演示代码:
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    /*
     UIUserNotificationTypeNone    = 0,      没有,没有本地通知
     UIUserNotificationTypeBadge   = 1 << 0, 接受图标右上角提醒数字
     UIUserNotificationTypeSound   = 1 << 1, 接受通知时候,可以发出音效
     UIUserNotificationTypeAlert   = 1 << 2, 接受提醒(横幅/弹窗)
     */
    // iOS8需要添加请求用户的授权
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
        [application registerUserNotificationSettings:settings];
    }
    if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
        // 跳转界面
    }
    return YES;
}
/**
 *  如果应用在后台,通过点击通知的时候打开应用会来到该代理方法
 *  如果应用在前台,接受到本地通知就会调用该方法
 *
 *  @param notification 通过哪一个通知来这里
 */
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    if (application.applicationState == UIApplicationStateActive) return;
    if (application.applicationState == UIApplicationStateInactive) {
        // 实现跳转
    }
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    return YES;
}
#import "ViewController.h"
@interface ViewController ()
// 点击按钮之后添加通知
- (IBAction)addLocalNote;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
- (IBAction)addLocalNote {
    // 1.创建本地通知
    UILocalNotification *localNote = [[UILocalNotification alloc] init];
    // 设置什么时间弹出
    localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
    // 设置弹出的内容
    localNote.alertBody = @"您有新消息";
    // 设置锁屏状态下,显示的一个文字
    localNote.alertAction = @"快点打开";
    // 是否显示alertAction的文字(默认是YES)
    localNote.hasAction = YES;
    // 设置音效
    localNote.soundName = UILocalNotificationDefaultSoundName;
    // 应用图标右上角的提醒数字
    localNote.applicationIconBadgeNumber = 1;
    // 设置UserInfo来传递信息
    localNote.userInfo = @{@"alertBody" : localNote.alertBody, @"applicationIconBadgeNumber" : @(localNote.applicationIconBadgeNumber)};
    // 2.调度通知
    [[UIApplication sharedApplication] scheduleLocalNotification:localNote];
}
远程推送通知
远程推送通知:就是通过网络从远程服务器推送给客户端的通知.
为什么需要远程推送通知? 
传统获取数据的局限性 
只要用户关闭了app,就无法跟app的服务器沟通,无法从服务器上获得最新的数据内容
远程推送通知可以解决以上问题 
不管用户打开还是关闭app,只要联网了,都能接收到服务器推送的远程通知
远程推送通知的使用 
所有的苹果设备,在联网状态下,都会与苹果的服务器建立长连接 
长连接: 只要联网了,就一直建立连接
长连接的作用: 时间校准, 系统升级, 查找我的iPhone
长连接的好处 : 数据传输速度快 , 数据保持最新状态
远程推送功能机制
苹果给iOS和Mac添加了消息推送的功能,使得我们可以通过后台服务器给应用程序(APP)发送消息,不管APP是否正在使用,比如邮箱的来件提示功能。这项服务被称为Apple Push Notification service(APNs)。里面一共涉及到四个角色:APP、设备、APNs和应用后台服务器(Provider),其中APP、后台服务器和APNs之间使用deviceToken唯一的标识一个用户。 
推送服务的工作流程:
APP向系统注册推送服务。
设备从APNs请求deviceToken。
通过代理方法将deviceToken返回给APP。
APP将deviceToken发送给应用后台服务器(Provider)。
应用后台服务器保存deviceToken,然后在需要推送通知的时候,给APNs发送信息,使用deviceToken标识所要送达的客户端。
APNs将后台服务器发过来的数据推送到设备。
设备将消息分发给应用程序。
在使用推送功能的时候,需要在开发者中心创建支持Push Notification的证书,并且将证书和私钥用于应用后台服务器与APNs之间通信。
我也写了一个比较易懂的远程推送流程图! http://blog.csdn.net/ismilesky/article/details/48324723
远程推送近年来,都是通过第三方进行实现,因为第三方推送的功能比较强大. 
推送平台: 百度推送 , 极光推送, 腾讯信鸽推送, 个推 
远程推送我们这里以极光推送(第三方)为例,进行测试.
极光远程推送的使用
如要使用极光第三方远程推送,我们需要集成iOS SDK,需要进行应用的配置,和环境配置.在开发者中心有 
developer.apple.com开发者账号 , iOS真机(iPhone、iPad、iPod)等。
远程推送应用配置过程:
这些相关配置极光推送已经给了非常详细的文档资料, iOS SDK集成指南 :http://docs.jpush.io/guideline/ios_guide/ , iOS SDK调试指南 :http://docs.jpush.io/client/ios_tutorials/#ios-sdk, 只需要按照流程进行就可以.
实现代码:
#define kDeviceVersion  ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
#import "AppDelegate.h"
#import "APService.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1
    if (kDeviceVersion) {
        //可以添加自定义categories
        [APService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:nil];
    } else {
        //categories 必须为nil
        [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];
    }
#else
    //categories 必须为nil
    [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];
#endif
    [APService setupWithOption:launchOptions];
    return YES;
}
#pragma mark - 获取device token (必须实现)
// 当得到苹果的APNs服务器返回的DeviceToken就会被调用
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    NSLog(@"%@",deviceToken);
    // Required
    [APService registerDeviceToken:deviceToken];
}
#pragma mark - 获取device token失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    NSLog(@"function == %s  line == %d  error == %@",__FUNCTION__,__LINE__,error);
}
// 接收到远程通知,触发方法和本地通知一致 (必须实现)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    // Required
    [APService handleRemoteNotification:userInfo];
}
#pragma mark - 使用后台的远程消息推送 (必须实现)
/**
 1> 在Capabilities中打开远程推送通知
 2> 实现该代理方法
远程消息数据格式:
 {"aps" : {"content-available" : 1},"content-id" : 42}
 执行completionHandler有两个目的
 1> 系统会估量App消耗的电量,并根据传递的UIBackgroundFetchResult 参数记录新数据是否可用
 2> 调用完成的处理代码时,应用的界面缩略图会自动更新
 注意:接收到远程通知到执行完网络请求之间的时间不能超过30秒
 if (userInfo) {
 int contentId = [userInfo[@"content-id"] intValue];
 ViewController *vc = (ViewController *)application.keyWindow.rootViewController;
 [vc loadDataWithContentID:contentId completion:^(NSArray *dataList) {
 vc.dataList = dataList;
 NSLog(@"刷新数据结束");
 completionHandler(UIBackgroundFetchResultNewData);
 }];
 } else {
 completionHandler(UIBackgroundFetchResultNoData);
 }
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // IOS 7 Support Required
    [APService handleRemoteNotification:userInfo];
    [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];
    // 判断应用程序在前台还是后台
    if (application.applicationState == UIApplicationStateActive) { // 活跃状态
       // 实现方法
    } else if (application.applicationState == UIApplicationStateInactive) {
        // 不活跃状态
        // 实现方法
    } else { // application.applicationState == UIApplicationStateBackground
        // 后台
        // 实现方法
    }
    /** 必须回调 */
    completionHandler(UIBackgroundFetchResultNewData);
}
测试时,运行完应用程序,要发送通知,进行推送的测试,真机收到通知才算成功. 
原文:http://www.cnblogs.com/W-Kr/p/5422290.html