Local/Romote_Notification

Apple 关于本地通知和远程通知的官方文档

简介

远程通知(remote notification)和本地通知(local notification)是两种用户通知类型,远程通知又称为推送通知(push notification)。这两种通知都可以实现当不在前台运行的APP通知使用该APP的用户新信息,这消息可以是消息,临近的日历,或来自远端服务器的新数据等。当操作系统呈现这些通知时,可以显示一个alert提醒或是app icon提醒。在提醒的时候也可以有声音的提示。

当用户点击通知时可以启动APP查看通知详情,也可选择不打开APP忽略通知。

注意

不管是远程通知合适本地通知都与广播通知(NSNotificationCenter)和KVO(key value observe)无关。

本地通知

Xcode 6本地通知接收不到

出现提示信息:

  1. Attempting to schedule a local notification
  2. with an alert but haven’t received permission from the user to display alerts
  3. with a sound but haven’t received permission from the user to play sounds

原因在于在iOS8系统上需要注册本地通知,这样才能正常使用

iOS8注册本地通知

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert categories:nil]];
    }

    return YES;
}

- (IBAction)registerLocationNotification:(id)sender {

UILocalNotification *locationNotification = [[UILocalNotification alloc]init];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5];
locationNotification.fireDate = date;
locationNotification.timeZone = [NSTimeZone defaultTimeZone];
locationNotification.repeatInterval = NSCalendarUnitSecond;
locationNotification.alertBody = @"通知来了。。。";
locationNotification.alertAction = @"通知";
locationNotification.alertLaunchImage = @"3.png";
locationNotification.userInfo = @{@"user":@"lfx"};
locationNotification.soundName = @"sound.caf";
locationNotification.applicationIconBadgeNumber = 10;
[[UIApplication sharedApplication] scheduleLocalNotification:locationNotification];
}

回调方法

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
    //取消通知的注册
    [application cancelLocalNotification:notification];
}

远程通知(Push通知)

APNS推送机制(Apple Push Notification Service)

image

Provider:程序的后台服务器。

上图分为三阶段:

第一阶段:应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。

第二阶段:APNS在自身的已注册Push服务的iPhone列表中,并把消息发送到iPhone。

第三阶段:iPhone把发来的消息传递给应用程序,并按照规定弹出Push通知。

APNS推送通知的详细流程

image

  1. APP注册APNS消息推送,iOS向APNS Server 请求获取device token
  2. APNS向APP发送device token ,APP接受device token
  3. APP向程序的服务器端发送device token
  4. 程序服务器端发送Push通知到APNS
  5. APNS向APP发送Push通知

远程推送操作过程

  1. APP启动过程中,注册远程通知
1
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert| UIRemoteNotificationTypeSound];
  1. 若注册成功,回调方法
1
2
3
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSLog(@"%@", deviceToken);
}
  1. App获取device token 后,将device token 发送到自己的服务端。
  2. APNS服务器得到JSON串后,向App发送通知消息,App调用回调方法
1
2
3
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
NSLog(@"%@", userInfo);//从userInfo获得推送信息内容
}

客户端的开发

cer文件 provisioning文件的生成

下载生成的cer文件和provi文件,双击导入Xcode,在build setting中code signing一栏中选择这两个文件的名称,这样就可以将支持push的app部署到真机中。然后消息推送:

客户端对推送消息的处理分为两种情况

  1. 在App没有运行的情况下,系统受到推送消息,用户点击推送消息,启动App。此时,不会执行前面提到的didReceiveRemoteNotification方法,而是App的applicationDidFinishLaunching方法中进行推送处理,通过以下代码可以获取推送消息中数据:

    Dictionary *userInfo =[launchOptions objectForKey:UIApplicationLaunchOp
    tionsRemoteNotificationKey];

  2. 当App处于前台,系统受到推送消息,此时系统不会推送消息提示,会直接触发application:(UIApplication )application didReceiveRemoteNotification:(NSDictionary )userInfo方法,推送数据在userInfo中。
    当App处于后台时,如果系统收到推送消息时,当用户点击推送消息时,会执行application: (UIApplication )application didReceiveRemoteNotification:(NSDictionary )userInfo方法,此时AppDelegate中方法的执行顺序为:applicationWillEnterForegroundapplicationWillEnterForegroundapplicationDidBecomeActive

LLDB

Xcode调试基础

如下图:调试视图包括顶部调试命令栏,左边变量视图,右边控制台。
调试器工具栏从左到右功能分别是:断点是否有效,跳到下一个断点,单步执行,进入方法内部,跳出当前方法到上一个方法

image

定制断点

满足条件的断点

image

i == 500 时代码暂停执行

image

根据以上选项可选择不同提示,Log Message 将消息写入日志,Sound 播放声音,若勾选Automatically continue after evaluating ,则会在播放声音或写入日志后不暂停执行代码

条件综合使用的断点

image

该断点的作用是:当 i == 500,打印self.view的信息,同时ls查看根路径下的目录,输出a的值,并且播放声音且不停止执行代码

断点类型

image

符号断点(Symbolic Breakpoint)

该断点用于捕获特定方法或函数的所有方法

image

image

捕获viewDidLoad方法

异常断点 (All Exceptions)

image

异常断点,在发生异常时触发。通常会通过设置一个全局的异常断点,可提供比崩溃更详细的栈跟踪信息。有时候,异常断点能直接指出崩溃的根源。调试期间可以始终确保有一个全局的异常断点是一种最佳的实践。

LLDB调试

  • p (printf)打印标量表达式(eg a+b)或结构(CGRect)的值。po (print object)打印对象及其描述,可以查看特定内存的地址,也可以指定对象名。
  • list X:打印指定行后的10行代码。通过参数 -X可以指定断点前多少行代码
  • step (S)下一行
  • continue(C)越过断点继续执行
  • fin 执行到当前方法末尾
  • kill 终止执行程序

image

background_task

简述

iOS于2008年⾯面世时,只能有⼀一个第三⽅方应⽤用处于活动状态(位于前台)。这意味着应⽤用需要执 ⾏行的任务都必须位于前台的时候完成,否则任务将暂停并在应⽤用下次启动的时候执⾏行。iOS4推 出后,向第三⽅方应⽤用提供了后台功能。鉴于iOS设备的系统资源有限,以及省电,对后台处理有 ⼀一些限制,就是不能干扰前台应⽤用,也不能消耗太多电量。

iOS支持后台任务处理方式:

  1. 在后台完成耗时任务。这种方式适用于完成大量数据的下载或更新等任务,他们需要的时间超过了用户与应用交互的时间。
  2. 执行iOS允许的后台活动,如播放音乐、与蓝牙设备互交、监测GPS数据以获得位置发生重大变化、位置永久性网络连接让VoIP型应用能够正常运行。

检查设备是否支持后台任务

在编写使用多任务功能的代码时,必须检查设备是否支持多任务

1
2
3
4
5
6
UIDevice* device = [UIDevice currentDevice];
if (! [device isMultitaskingSupported])
{
NSLog(@"当前设备不⽀支持多任务.");
return;
}

在后台完成任务

要在后台执⾏行任务,应告诉应⽤用这项任务需要能够在后台运⾏行。另外,还应考虑需要多少内存 以及多⻓长时间才能完成。如果任务完成需要的时间超过10-15分钟,很可能任务还未完成应⽤用就 被终⽌止了。所以任务应该包含处理提前终⽌止的逻辑,并能够在重新启动应⽤用后继续执⾏行。操作 系统给应⽤用指定⼀一段时间,供它完成后台任务,但如果如果操作系统发现资源紧缺,也可能提 早终⽌止应⽤用。

后台任务的通用执行过程:

  1. 向应用请求后台任务标识符,并指定一个用来处理终止程序的快。
  2. 仅当应⽤用完了后台时间,或系统发现资源使用率过⾼进⽽决定终⽌应⽤时,终⽌处理程序才会被调⽤用。
  3. 执⾏后台任务逻辑。这包括从请求后台任务标识符到结束后台任务之间的所有代码。
  4. 让应用结束后台任务,并让后台标识符失效。
获取后台任务标识符

获取后台任务标识符同时制定终止处理程序块,使用__block 是为了block内部要对其修饰的变量修改。

1
2
3
4
5
__block NSInteger counter = 0;
__block UIBackgroundTaskIndentifier bTaskIndentifier = [[UIApplication shareApplication] beginBackgroundTaskWithExpirationHandler:
^{
...
}];
终止处理程序

系统在指定的时间或资源⽤完后,将调⽤后台任务的终止处理程序。应用即将关闭前,将在主程中调⽤用终处理程序。给终⽌止处理程序提供的时间不多(最多几秒),所以它执⾏行的操作应尽可能少。

1
2
3
4
5
__block UIBackgroundTaskIdentifier bTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:bTask];//让应用指导后台任务结束
bTask = UIBackgroundTaskInvalid;//让后台任务标识符失效
}];
执行后台任务

获取后台任务标⽰符后,就可以开始在后台执⾏行任务了。再执⾏行后台任务过程中,注意从应⽤那⾥获取后台任务余下的时间。可据此决定是否接着执⾏其他的代码。通常在余下的时间还有几秒时终⽌止后台任务,让它有时间执⾏行扫尾工作。后台任务完成后,还需要做两项重要⼯作: 1. 对共享应⽤用实例调用endBackgroundTask 让应⽤用知道后台任务已结束;2. 将变量bTask设置为UIBackgroundTaskInvalid ,让这个后台任务标⽰示符⽆无效。从获取后台任务标示符到结束后台任务之间的所有代码将在后台执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
__block UIBackgroundTaskIdentifier bTask =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication] endBackgroundTask:bTask];//让应用指导后台任务结束
bTask = UIBackgroundTaskInvalid;//让后台任务标识符失效
}];
NSInteger timeDuration = 1 * 60;
NSLog (@"Background task starting , task ID is %u",bTask);
for (int counter = 0; counter <= timeDuration;counter ++ )
{
[NSThread sleepForTimeInterval:1];
//剩余运行时间
NSTimerInterval remainingTime = [[UIApplication sharedApplication] backgroundTimeRemaining];
if (remainingTime == DBL_MA){
NSLog(@"Background Processed %d. Still in foreground.",counter);
}else {
NSLog(@"Background Processed %d. Time remaining is: %f",counter,remainingTime);
}
}
dispatch_sync(dispatch_get_main_queue()
{
[self.backgroundButton setEnabled:YES];
[self.backgroundButton setTitle:@"Start Background Task" forState:UIControlStateNorm];
}
[[UIApplication sharedApplication] endBackgroundTask:bTask];
bTask = UIBackgroundTaskInvalid;

实现后台任务

iOS⽀支持⼀组具体的后台任务,这些活动可不断地进⾏下去,不像使⽤后台任务标示符那样受到限制。这些活动不受时间上的限制,注意这些活动不应使用过多的系统资源,以避免被操作系统强⾏终⽌止。

后台活动类型
  • 后台播放音频
  • 跟踪设备位置
  • 支持ip语音应用
  • 现在‘报刊杂志’应用的新内容
  • 与外置或蓝牙配件通信
  • 在后台取回内容
  • 使用推送通知启动后台下载

SVN

svn 查看log

$ svn log

查看SVN信息

$ svn info

查看当前SVN关联目录下的文件改动

$ svn st

提交代码

$ svn ci file.m file2.m -m “提交file.m 和 file2.m”

比较某个文件与SVN上代码的改动

$ svn diff file.m //查看file.m 的变化,此命令只显示代码段的不同

注释 比较两个文件或两个工程的差异,可使用Xcode工具filemerge
打开方式:右击Xcode -> open developer tool -> filemerge

删除svn服务器代码的某个文件

$ svn del file.m //删除file.m这个文件,之后需要svn ci -m “提交删除的文件”

增加一个新文件

$ svn add file.m //并提交

注意
添加或删除某些名称中带@符号的文件,需要在其后面加个@,
eg:添加scree@2x.png的命令
$ svn add screen@2x.png@

cocoapods

简介

cocoapods

CocoaPods项目源码

安装

  1. 使用MAC自带的ruby下的gem命令:
1
2
$ sudo gem install cocoapods
$ pod setup
在执行pod setup时,会输出Setting up CocoaPods master repo,但是会执行很长时间,在此期间CocoaPods将它的信息下载到~/.cocoapods目录下,此时可以cd到该目录,使用du -sh 命令查看下载进度。使用镜像索引可以提高下载速度(下面有介绍)。
  1. 将ruby源替换成国内淘宝源
1
2
3
4
$ gem sources --remove https://rubygems.org/
$ gem sources -a http://ruby.taobao.org/
查看源是否替换成功
$ gem sources -l
  1. 如果安装的CocoaPods不能使用,可以尝试更新gem (sudo gem update —system),更新Iterm(删除原有的iterm,下载并安装新版本的iterm)。

镜像索引

所有的项目的PodSpec文件都托管在podSpec。第一次执行pod setup时,将这些索引文件更新到本地~/.cocoapods/目录下。该文件比较大,所以第一次更新比较慢。

akinkiu在gitcafeocchina上建立了CocoaPods索引镜像。gitcafe和occhina都是国内服务器,在执行索引更新操作时会比较快。

CoaoaPods设置成gitcafe镜像:

1
2
3
$ pod repo remove master
$ pod repo add master https://gitcafe.com/akuandev/Specs.git
$ pod repo update

将以上url改为http://git.oschina.net.akuandev/Specs.git即可使用occhina镜像。

用法

  1. cd到以创建的项目的目录下执行以下命令

    • pod init (生成Podfile文件,编辑该文件,添加所要加入的第三方库的名称版本(版本可以不用加))

      eg:

      pod ‘AFNetworking’, ‘~> 2.0’

      pod ‘JSONKit’

    • pod install (自动将加入的第三方库源码下载下来,且为项目设置号好相应的系统依赖和编译参数)

      注意

    • 使用CocoaPods生成的*.xcworkspace文件打开工程,而不是以前的*.xcodeproj文件。

    • 每次修改了Podfile文件,都需要执行一次pod update命令。

    • pop install之后还生成的另一个文件Podfile.lock,该文件不能放在.gitignore文件中。该文件用来锁定当前各依赖库的版本,之后及时多次执行pod install也不会更改版本,只有执行pod update才会改变Podfile.lock文件。该文件可以在多人协作时,防止第三方库升级造成大家各自的第三方库的版本不一致。Podfile.lock

  2. 查找第三方库

查找CocoaPods管理是否有你想要的库,执行命令 pod search

eg: pod search json 可查找与接送有关的所有库

  1. 生成第三方库帮助文档

使用CocoaPods生成第三方库的帮助文档并集成到Xcode中,可使用brew安装appledoc即可:

1
brew install appledoc

其优点:将帮助文档集成到Xcode中,在敲代码时可以按住opt键查看相应帮助文档

  1. 不更新podspec

CocoaPods在执行pod install 和 pod update 时,会默认更新podspec索引。禁止更新podspec的命令:

1
2
$ pod install --no-repo-update
$ pod update --no-repo-update

原理

CocoaPods的原理是将所有的依赖库都放到一个名为Pods的项目中,然互让主项目依赖Pods项目。

  1. Pods项目最终会编译成一个名为libPods.a的文件,主项目只需要依赖.a这个文件即可。
  2. 对于资源文件,CocoaPods提供了一个名为Pods-resources.sh的bash脚本,该脚本在每次项目编译的时候都会执行,将第三方的各个资源文件复制到目标目录中。
  3. CocoaPods通过一个名为Pods.xcconfig的文件在编译的时候设置所有的参数和依赖。

viewController

1.UIViewController生命周期几种方法调用时机

  1. - (id)initWithNibName:(NSString )nibNameOrNil bundle:(NSBundle )nibBundleOrNil;
    当UIViewController在初始化加载xib文件时,该方法被调用。
  2. - (void)loadView;
    当每次访问UIViewController的view(比如controller.view,self.view)并且view为nil,该方法就会被调用。
  3. - (void)viewWillUnload;
  4. - (void)viewDidUnload;
  5. - (void)viewDidLoad;
    不管是通过xib文件还是重写loadView创建UIViewController的view,在view创建完毕后,最终都会调用该方法。
  6. - (void)viewWillAppear:(BOOL)animated;
    当view将要呈现在UI界面上时会调用该方法。
  7. - (void)viewDidAppear:(BOOL)animated;
    当view已经完全过渡到屏幕上时会调用该方法。
  8. - (void)viewWillDisappear:(BOOL)animated;
    当view将要消失,或被覆盖、隐藏时会调用该方法。
  9. - (void)viewDidDisappear:(BOOL)animated;
    当view已经完全消失,或被覆盖、隐藏时会调用该方法。

2.UIViewController xib的加载过程

loadView默认实现就是在自定义的UIViewController类里不重写loadView方法,或者重写后只是在方法里简单实现[super loadView]。默认实现做了如下几个工作:

  1. 它会先去查找与UIViewController相关联的xib文件,通过加载xib文件来创建UIViewController的view
    如果在初始化UIViewController指定了xib文件名,就会根据传入的xib文件名加载对应的xib文件
    [[UIViewController alloc] initWithNibName:@”ViewController” bundle:nil];
    如果没有明显地传xib文件名,就会加载跟UIViewController同名的xib文件
    [[UIViewController alloc] init]; // 加载UIViewController.xib
  2. 如果没有找到相关联的xib文件,就会创建一个空白的UIView,然后赋值给UIViewController的view属性,大致如下:
    self.view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame] autorelease];

3.几种控制器容器的优缺点

1.UITabBarController(Tabbar底部标签式:Instagram、微信、微博)

UITabBarController作为一种容器适合管理底部标签式(TabBar 如: Instagram、微信、微博)这种结构的APP

优点

  1. 清楚当前所在的入口位置
  2. 轻松在各入口间频繁跳转且不会迷失方向
  3. 直接展现最重要入口的内容信息(默认首页)

缺点

功能入口过多时,该模式显得笨重不实用(超过5个的时候会将其他控制器添加到一个更多界面)

2.UINavigationController (Springboard跳板式:星巴克)

UINavigationController作为一种容器适合管理跳板式(SpringBoard 如: 星巴克)这种结构的APP

优点

  1. 清晰展现各入口
  2. 容易记住各入口位置,方便快速找到

确定

  1. 无法在多入口间灵活跳转,不适合任务操作
  2. 容易形成更深的路径
  3. 不能直接展现入口内容
  4. 不能显示太多入口次级内容

3.UINavigationController做整体,融合TabBarController(List Menu列表式:知乎)

优点

  1. 层次展示清晰
  2. 可展示内容较长的标题
  3. 可展示标题的次级内容

缺点

  1. 同级内容过多时,用户浏览容易产生疲劳
  2. 排版灵活性不是很高
  3. 只能通过排列顺序、颜色来区分各入口重要程度

4.UIScrollvIewController融合pageControl (Carousel旋转木马式:豆瓣同城)

优点

  1. 单页面内容整体性强
  2. 线性的浏览方式有顺畅感、方向感

缺点

  1. 不适合展示过多页面
  2. 不能跳跃性地查看间隔的页面,只能按顺序查看相邻的页面
  3. 由于各页面内容结构相似,容易忽略后面的内容

5. Drawer抽屉式:豆瓣小组

优点

  1. 兼容多种模式(页面内自有组织)
  2. 扩展性好

缺点

  1. 隐藏框架中其他入口
  2. 对入口交互的功能可见性(affordance)要求高
  3. 对排版要求高

6. Plus点聚式:Path

优点

  1. 灵活
  2. 展示方式有趣
  3. 使界面更开阔

缺点

  1. 隐藏框架中其他入口
  2. 对入口交互的功能可见性(affordance)要求高

7.Gallery陈列馆式:多看,豆瓣电影

优点

  1. 直观展现各项内容
  2. 方便浏览经常更新的内容

缺点

  1. 不适合展现顶层入口框架
  2. 容易形成界面内容过多,显得杂乱
  3. 设计效果容易呆板

8.Waterfall瀑布式

优点

  1. 浏览时产生流畅体验

缺点

  1. 缺乏对整体内容的体积感,容易发生空间位置迷失
  2. 浏览一段时间后,容易产生疲劳感