oc常见误区

时间:2022-07-08 22:45:42

1、同步请求可以从因特网请求数据,一旦发送同步请求,程序将停止用户交互,直至服务器返回数据完成,才可以进行下一步操作,

2、异步请求不会阻塞主线程,而会建立一个新的线程来操作,用户发出异步请求后,依然可以对UI进行操作,程序可以继续运行

3、GET请求,将参数直接写在访问路径上。操作简单,不过容易被外界看到,安全性不高,地址最多255字节;

4、POST请求,将参数放到body里面。POST请求操作相对复杂,需要将参数和地址分开,不过安全性高,参数放在body里面,不易被捕获。

查看源码打印?

001

1、     同步GET请求

//第一步,创建URL

NSURL*url = [NSURL URLWithString:@"http://api.hudong.com/iphonexml.do?type=focus-c"];

//第二步,通过URL创建网络请求

NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

//NSURLRequest初始化方法第一个参数:请求访问路径,第二个参数:缓存协议,第三个参数:网络请求超时时间(秒)

其中缓存协议是个枚举类型包含:

NSURLRequestUseProtocolCachePolicy(基础策略)

NSURLRequestReloadIgnoringLocalCacheData(忽略本地缓存)

NSURLRequestReturnCacheDataElseLoad(首先使用缓存,如果没有本地缓存,才从原地址下载)

NSURLRequestReturnCacheDataDontLoad(使用本地缓存,从不下载,如果本地没有缓存,则请求失败,此策略多用于离线操作)

NSURLRequestReloadIgnoringLocalAndRemoteCacheData(无视任何缓存策略,无论是本地的还是远程的,总是从原地址重新下载)

NSURLRequestReloadRevalidatingCacheData(如果本地缓存是有效的则不下载,其他任何情况都从原地址重新下载)

//第三步,连接服务器

NSData *received = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

NSString *str = [[NSString alloc]initWithData:received encoding:NSUTF8StringEncoding];

NSLog(@"%@",str);

2、同步POST请求

//第一步,创建URL

NSURL *url = [NSURL URLWithString:@"http://api.hudong.com/iphonexml.do"];

//第二步,创建请求

NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

[request setHTTPMethod:@"POST"];//设置请求方式为POST,默认为GET

NSString *str = @"type=focus-c";//设置参数

NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];

[request setHTTPBody:data];

//第三步,连接服务器

NSData *received = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

NSString *str1 = [[NSString alloc]initWithData:received encoding:NSUTF8StringEncoding];

NSLog(@"%@",str1);

072

3、异步GET请求

//第一步,创建url

NSURL *url = [NSURL URLWithString:@"http://api.hudong.com/iphonexml.do?type=focus-c"];

//第二步,创建请求

NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

//第三步,连接服务器

NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];

085

4、异步POST请求

//第一步,创建url

NSURL *url = [NSURL URLWithString:@"http://api.hudong.com/iphonexml.do"];

//第二步,创建请求

NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];

[request setHTTPMethod:@"POST"];

NSString *str = @"type=focus-c";

NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];

[request setHTTPBody:data];

//第三步,连接服务器

NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];

5、异步请求的代理方法

//接收到服务器回应的时候调用此方法

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

{

NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

NSLog(@"%@",[res allHeaderFields]);

self.receiveData = [NSMutableData data];

}

//接收到服务器传输数据的时候调用,此方法根据数据大小执行若干次

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{

[self.receiveData appendData:data];

}

//数据传完之后调用此方法

-(void)connectionDidFinishLoading:(NSURLConnection *)connection

{

NSString *receiveStr = [[NSString alloc]initWithData:self.receiveData encoding:NSUTF8StringEncoding];

NSLog(@"%@",receiveStr);

}

//网络请求过程中,出现任何错误(断网,连接超时等)会进入此方法

-(void)connection:(NSURLConnection *)connection

didFailWithError:(NSError *)error

{

NSLog(@"%@",[error localizedDescription]); }

从表面的意思看get 和 post的区别get就是获取数据,post就是发送数据。这个是误区。其实两者都可以的,在IOS向服务器发送请求里面可以带参数。

那么这些误区是怎么出现的呢?先看看一下对http的解释

一般在浏览器中输入网址访问资源都是通过GET方式;在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交

Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE

URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查 ,改 ,增 ,删 4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询 资源信息,而POST一般用于更新 资源信息(个人认为这是GET和POST的本质区别,也是协议设计者的本意,其它区别都是具体表现形式的差异 )。

再进一步了解下他们两个的区别:

1. GET使用URL或Cookie传参。而POST将数据放在BODY中。

2. GET的URL会有长度上的限制,则POST的数据则可以非常大。

3. POST比GET安全,因为数据在地址栏上不可见。

这些也是有点误区的,就像同步请求一定的慢吗?

GET和POST与数据如何传递没有关系?

GET和POST是由HTTP协议定义的。在HTTP协议中,Method和Data(URL, Body, Header)是正交的两个概念,也就是说,使用哪个Method与应用层的数据如何传输是没有相互关系的。

HTTP没有要求,如果Method是POST数据就要放在BODY中。也没有要求,如果Method是GET,数据(参数)就一定要放在URL中而不能放在BODY中。

那么,网上流传甚广的这个说法是从何而来的呢?我在HTML标准中,找到了相似的描述。这和网上流传的说法一致。但是这只是HTML标准对HTTP协议的用法的约定。怎么能当成GET和POST的区别呢?

而且,现代的Web Server都是支持GET中包含BODY这样的请求。虽然这种请求不可能从浏览器发出,但是现在的Web Server又不是只给浏览器用,已经完全地超出了HTML服务器的范畴了。

HTTP协议对GET和POST都没有对长度的限制?

HTTP协议明确地指出了,HTTP头和Body都没有长度的要求。而对于URL长度上的限制,有两方面的原因造成:

1. 浏览器。据说早期的浏览器会对URL长度做限制。据说IE对URL长度会限制在2048个字符内(流传很广,而且无数同事都表示认同)。但我自己试了一下,我构造了90K的URL通过IE9访问live.com,是正常的。网上的东西,哪怕是Wikipedia上的,也不能信。

2. 服务器。URL长了,对服务器处理也是一种负担。原本一个会话就没有多少数据,现在如果有人恶意地构造几个几M大小的URL,并不停地访问你的服务器。服务器的最大并发数显然会下降。另一种攻击方式是,把告诉服务器Content-Length是一个很大的数,然后只给服务器发一点儿数据,嘿嘿,服务器你就傻等着去吧。哪怕你有超时设置,这种故意的次次访问超时也能让服务器吃不了兜着走。有鉴于此,多数服务器出于安全啦、稳定啦方面的考虑,会给URL长度加限制。但是这个限制是针对所有HTTP请求的,与GET、POST没有关系。

这个貌似听着对点吧。

3.对于安全不安全讲。

get:

.所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。

* 注意:这里安全的含义仅仅是指是非修改信息。

POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存, (2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击 .

看出来区别了吧

单例创建:

创建一个单例很多办法。我先列举一个苹果官方文档中的写法。

static AccountManager *DefaultManager = nil;

+ (AccountManager *)defaultManager {

  1. if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init];
  2. return DefaultManager;
  3. }

当然,在iOS4之后有了另外一种写法:

+ (AccountManager *)sharedManager

{

static AccountManager *sharedAccountManagerInstance = nil;

static dispatch_once_t predicate;

dispatch_once(&predicate, ^{

sharedAccountManagerInstance = [[self alloc] init];

});

return sharedAccountManagerInstance;

}

该写法来自 objcolumnist,文中提到,该写法具有以下几个特性:

1. 线程安全。

2. 满足静态分析器的要求。

3. 兼容了ARC

http://duxinfeng.com/2015/07/14/iOS%E5%BC%80%E6%BA%90App%E6%95%B4%E7%90%86/

使用支付宝进行一个完整的支付功能,大致有以下步骤:

  • 与支付宝签约,获得商户ID(partner)和账号ID(seller)
  • 下载相应的公钥私钥文件(加密签名用)
  • 下载支付宝SDK
  • 生成订单信息
  • 调用支付宝客户端,由支付宝客户端跟支付宝安全服务器打交道
  • 支付完毕后返回支付结果给商户客户端和服务器

支付宝提供了Demo让开发人员快速了解支付的接入流程:http://club.alipay.com/thread.php?fid=703,遇到技术上的问题也以到论坛提问

假设签约之类工作已经完成,我们开干

要想集成支付功能,依赖以下文件夹的库文件(把这3个添加到你的客户端中)

接口调用步骤

1.封装订单模型

AlixPayOrder *order = [[AlixPayOrder alloc] init];

// 生成订单描述

NSString *orderSpec = [order description];

2.签名

id<DataSigner> signer = CreateRSADataSigner(@“私钥key”);

// 传入订单描述 进行 签名

NSString *signedString = [signer signString:orderSpec];

3.生成订单字符串

NSString *orderString = [NSString stringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",

orderSpec, signedString, @"RSA"];

4.调用支付接口

AlixPay * alixpay = [AlixPay shared];

// appScheme:商户自己的协议头

int ret = [alixpay pay:orderString applicationScheme:appScheme];

现在随着移动开发的快速发展,越来越多的应用要求在线支付功能。最近做了一个关于支付宝支付功能的应用,在使用支付宝的过程中,遇到一些不必要的弯路,因此,写了这篇文章总结一下关于ios开发如何使用支付宝。

正文

首先,我们需要支付宝的功能,应该去支付宝的开发平台,下载sdk以及demo。地址:点击进入下载页面

第一步、将需要的文件,静态库等拖入工程中,这里有:include,libs,Utilities,libcrypto.a,libssl.a文件。

如图:

上面就是我们所要得到的结果。

第二步、设置Head Search Paths 和 Library Search Paths。

targets->Build Setting ->Search Paths->Head Search Paths和 Library Search Paths

切记:Head Search Paths 和 Library Search Paths的路径一定要根据这个地址能找到对应的头文件。不然设置总会有这个错误(我就在这儿耗了很多时间),如图:

#include <openssl/rsa.h> 'openssl/rsa.h' file not found

下面是我设置的正确路径,如图:

好的,这里设置已经完成了,编译成功。

第三步、设置partner、seller、RSA private key、RSA public key。

我这里和官方demo一样都是设置在-info文件中,当然,你也可以自行决定放在其他地方。

如图:

这里的参数来自其他地方。。

第四步、支付成功后,回到应用程序中。

注意代码中有这句代码:

  1. //应用注册scheme,在AlixPayDemo-Info.plist定义URL types,用于安全支付成功后重新唤起商户应用
  2. NSString *appScheme = @"paytest";

所以,我们还需要设置回调的标示。

同样在-info文件中,添加个节点。如图:

注意:上面的item0的值就是appScheme。

(1)官方下载ShareSDK iOS 2.8.8,地址:http://sharesdk.cn/

(2)根据实际情况,引入相关的库,参考官方文档

(3)在项目的AppDelegate中一般情况下有三个操作,第一是注册ShareSDK,第二是注册各个平台的账号,第三是关于微信等应用的回调处理。

  1. //
  2. //  AppDelegate.m
  3. //  ShareSDKTest
  4. //
  5. //  Created by wangdalei on 14-6-23.
  6. //  Copyright (c) 2014年 王大雷. All rights reserved.
  7. //
  8. #import "AppDelegate.h"
  9. #import "RootViewController.h"
  10. #import <ShareSDK/ShareSDK.h>
  11. #import "WeiboApi.h"
  12. #import <TencentOpenAPI/QQApiInterface.h>
  13. #import <TencentOpenAPI/TencentOAuth.h>
  14. #import "WXApi.h"
  15. #import <TencentOpenAPI/QQApiInterface.h>
  16. #import <TencentOpenAPI/TencentOAuth.h>
  17. @implementation AppDelegate
  18. @synthesize rootVC;
  19. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  20. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  21. if (self.rootVC==nil) {
  22. self.rootVC = [[RootViewController alloc]initWithNibName:@"RootViewController" bundle:nil];
  23. }
  24. UINavigationController *rootNav = [[UINavigationController alloc]initWithRootViewController:self.rootVC];
  25. self.window.rootViewController = rootNav;
  26. self.window.backgroundColor = [UIColor whiteColor];
  27. [self.window makeKeyAndVisible];
  28. <span style="color:#ff0000;">[ShareSDK registerApp:@"1a2e7ab5fb6c"];</span>
  29. <span style="color:#3366ff;"> //添加新浪微博应用 注册网址 http://open.weibo.com  wdl@pmmq.com 此处需要替换成自己应用的
  30. [ShareSDK connectSinaWeiboWithAppKey:@"3201194191"
  31. appSecret:@"0334252914651e8f76bad63337b3b78f"
  32. redirectUri:@"http://appgo.cn"];
  33. //添加腾讯微博应用 注册网址 http://dev.t.qq.com wdl@pmmq.com 此处需要替换成自己应用的
  34. [ShareSDK connectTencentWeiboWithAppKey:@"801307650"
  35. appSecret:@"ae36f4ee3946e1cbb98d6965b0b2ff5c"
  36. redirectUri:@"http://www.sharesdk.cn"
  37. wbApiCls:[WeiboApi class]];
  38. //添加QQ空间应用 注册网址  http://connect.qq.com/intro/login/ wdl@pmmq.com 此处需要替换成自己应用的
  39. [ShareSDK connectQZoneWithAppKey:@"100371282"
  40. appSecret:@"aed9b0303e3ed1e27bae87c33761161d"
  41. qqApiInterfaceCls:[QQApiInterface class]
  42. tencentOAuthCls:[TencentOAuth class]];
  43. //此参数为申请的微信AppID wdl@pmmq.com 此处需要替换成自己应用的
  44. [ShareSDK connectWeChatWithAppId:@"wx4868b35061f87885" wechatCls:[WXApi class]];
  45. //添加QQ应用 该参数填入申请的QQ AppId wdl@pmmq.com 此处需要替换成自己应用的
  46. [ShareSDK connectQQWithQZoneAppKey:@"100371282"
  47. qqApiInterfaceCls:[QQApiInterface class]
  48. tencentOAuthCls:[TencentOAuth class]];</span>
  49. return YES;
  50. }
  51. - (void)applicationWillResignActive:(UIApplication *)application {
  52. // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
  53. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
  54. }
  55. - (void)applicationDidEnterBackground:(UIApplication *)application {
  56. // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
  57. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
  58. }
  59. - (void)applicationWillEnterForeground:(UIApplication *)application {
  60. // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
  61. }
  62. - (void)applicationDidBecomeActive:(UIApplication *)application {
  63. // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
  64. }
  65. - (void)applicationWillTerminate:(UIApplication *)application {
  66. // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
  67. }
  68. <span style="color:#ff6600;">#pragma mark - WX回调
  69. - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
  70. return [ShareSDK handleOpenURL:url wxDelegate:self];
  71. }
  72. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  73. return [ShareSDK handleOpenURL:url sourceApplication:sourceApplication annotation:annotation wxDelegate:self];
  74. }
  75. #pragma mark - WXApiDelegate
  76. /*! @brief 收到一个来自微信的请求,第三方应用程序处理完后调用sendResp向微信发送结果
  77. *
  78. * 收到一个来自微信的请求,异步处理完成后必须调用sendResp发送处理结果给微信。
  79. * 可能收到的请求有GetMessageFromWXReq、ShowMessageFromWXReq等。
  80. * @param req 具体请求内容,是自动释放的
  81. */
  82. -(void) onReq:(BaseReq*)req{
  83. }
  84. /*! @brief 发送一个sendReq后,收到微信的回应
  85. *
  86. * 收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。
  87. * 可能收到的处理结果有SendMessageToWXResp、SendAuthResp等。
  88. * @param resp具体的回应内容,是自动释放的
  89. */
  90. -(void) onResp:(BaseResp*)resp{
  91. }
  92. </span>
  93. @end

(4)信息分享。

  1. -(IBAction)share:(id)sender{
  2. NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"card"  ofType:@"png"];
  3. //构造分享内容
  4. id<ISSContent> publishContent = [ShareSDK content:@"分享内容测试"
  5. defaultContent:@"默认分享内容测试,没内容时显示"
  6. image:[ShareSDK imageWithPath:imagePath]
  7. title:@"pmmq"
  8. url:@"http://www.sharesdk.cn"
  9. description:@"这是一条测试信息"
  10. mediaType:SSPublishContentMediaTypeNews];
  11. [ShareSDK showShareActionSheet:nil
  12. shareList:nil
  13. content:publishContent
  14. statusBarTips:YES
  15. authOptions:nil
  16. shareOptions: nil
  17. result:^(ShareType type, SSResponseState state, id<ISSPlatformShareInfo> statusInfo, id<ICMErrorInfo> error, BOOL end) {
  18. if (state == SSResponseStateSuccess)
  19. {
  20. NSLog(@"分享成功");
  21. }
  22. else if (state == SSResponseStateFail)
  23. {
  24. NSLog(@"分享失败");
  25. }
  26. }];
  27. }

(5)登录、登出、获取授权信息、关注制定微博

  1. //
  2. //  LoginViewController.m
  3. //  ShareSDKTest
  4. //
  5. //  Created by wangdalei on 14-6-23.
  6. //  Copyright (c) 2014年 王大雷. All rights reserved.
  7. //
  8. #import "LoginViewController.h"
  9. #import <ShareSDK/ShareSDK.h>
  10. @interface LoginViewController ()
  11. -(IBAction)loginWithSina:(id)sender;
  12. -(IBAction)loginWithQQ:(id)sender;
  13. -(IBAction)loginoutWithSina:(id)sender;
  14. -(IBAction)loginoutWithQQ:(id)sender;
  15. -(IBAction)guanzhuUs:(id)sender;
  16. -(void)reloadStateWithType:(ShareType)type;
  17. @end
  18. @implementation LoginViewController
  19. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  20. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  21. if (self) {
  22. }
  23. return self;
  24. }
  25. - (void)viewDidLoad {
  26. [super viewDidLoad];
  27. }
  28. - (void)didReceiveMemoryWarning {
  29. [super didReceiveMemoryWarning];
  30. }
  31. - (IBAction)loginWithSina:(id)sender {
  32. [ShareSDK getUserInfoWithType:ShareTypeSinaWeibo authOptions:nil result:^(BOOL result, id<ISSPlatformUser> userInfo, id<ICMErrorInfo> error) {
  33. NSLog(@"%d",result);
  34. if (result) {
  35. //成功登录后,判断该用户的ID是否在自己的数据库中。
  36. //如果有直接登录,没有就将该用户的ID和相关资料在数据库中创建新用户。
  37. [self reloadStateWithType:ShareTypeSinaWeibo];
  38. }
  39. }];
  40. }
  41. -(IBAction)loginWithQQ:(id)sender{
  42. [ShareSDK getUserInfoWithType:ShareTypeQQSpace authOptions:nil result:^(BOOL result, id<ISSPlatformUser> userInfo, id<ICMErrorInfo> error) {
  43. NSLog(@"%d",result);
  44. if (result) {
  45. //成功登录后,判断该用户的ID是否在自己的数据库中。
  46. //如果有直接登录,没有就将该用户的ID和相关资料在数据库中创建新用户。
  47. [self reloadStateWithType:ShareTypeQQSpace];
  48. }
  49. }];
  50. }
  51. -(IBAction)loginoutWithSina:(id)sender{
  52. [ShareSDK cancelAuthWithType:ShareTypeSinaWeibo];
  53. [self reloadStateWithType:ShareTypeSinaWeibo];
  54. }
  55. -(IBAction)loginoutWithQQ:(id)sender{
  56. [ShareSDK cancelAuthWithType:ShareTypeQQSpace];
  57. [self reloadStateWithType:ShareTypeQQSpace];
  58. }
  59. -(void)reloadStateWithType:(ShareType)type{
  60. //现实授权信息,包括授权ID、授权有效期等。
  61. //此处可以在用户进入应用的时候直接调用,如授权信息不为空且不过期可帮用户自动实现登录。
  62. id<ISSPlatformCredential> credential = [ShareSDK getCredentialWithType:type];
  63. UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"TEXT_TIPS", @"提示")
  64. message:[NSString stringWithFormat:
  65. @"uid = %@\ntoken = %@\nsecret = %@\n expired = %@\nextInfo = %@",
  66. [credential uid],
  67. [credential token],
  68. [credential secret],
  69. [credential expired],
  70. [credential extInfo]]
  71. delegate:nil
  72. cancelButtonTitle:NSLocalizedString(@"TEXT_KNOW", @"知道了")
  73. otherButtonTitles:nil];
  74. [alertView show];
  75. }
  76. //关注用户
  77. -(IBAction)guanzhuUs:(id)sender{
  78. [ShareSDK followUserWithType:ShareTypeSinaWeibo         //平台类型
  79. field:@"ShareSDK"                //关注用户的名称或ID
  80. fieldType:SSUserFieldTypeName        //字段类型,用于指定第二个参数是名称还是ID
  81. authOptions:nil                        //授权选项
  82. viewDelegate:nil                        //授权视图委托
  83. result:^(SSResponseState state, id<ISSPlatformUser> userInfo, id<ICMErrorInfo> error) {
  84. if (state == SSResponseStateSuccess) {
  85. NSLog(@"关注成功");
  86. } else if (state == SSResponseStateFail) {
  87. NSLog(@"%@", [NSString stringWithFormat:@"关注失败:%@", error.errorDescription]);
  88. }
  89. }];
  90. }
  91. @end

(5)你可能会看到一些应用需要第三方登录的,一种是弹出webView加载的新浪微博或者qq的网页授权,还有一种是跳转到本地的已经安装的新浪微博应用或者qq应用进行授权。第二种授权方式较SSO授权,体验会比较好一些,因为不需要用户输入新浪微博或QQ的用户名与密码。

第二种授权方式需要在plist中配置Scheme。SSO默认是打开的不需要配置。在AppDelegate中实现回调。

(6)测试DEMO截图:

AFNetworking是一个讨人喜欢的网络库,适用于iOS以及Mac OS X. 它构建于在NSURLConnectionNSOperation, 以及其他熟悉的Foundation技术之上. 它拥有良好的架构,丰富的api,以及模块化构建方式,使得使用起来非常轻松.例如,他可以使用很轻松的方式从一个url来得到json数据:

1

NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/statuses/public_timeline.json"];

2

NSURLRequest *request = [NSURLRequest requestWithURL:url];

3

AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {

4

NSLog(@"Public Timeline: %@", JSON);

5

} failure:nil];

6

[operation start];

综述

CORE:

AFURLConnectionOperation:一个 NSOperation 实现了NSURLConnection 的代理方法.

HTTP Requests:

AFHTTPRequestOperation:AFURLConnectionOperation的子类,当request使用的协议为HTTP和HTTPS时,它压缩了用于决定request是否成功的状态码和内容类型.

AFJSONRequestOperation:AFHTTPRequestOperation的一个子类,用于下载和处理jason response数据.

AFXMLRequestOperation:AFHTTPRequestOperation的一个子类,用于下载和处理xml response数据.

AFPropertyListRequestOperation:AFHTTPRequestOperation的一个子类,用于下载和处理property list response数据.

HTTP CLIENT:

AFHTTPClient:捕获一个基于http协议的网络应用程序的公共交流模式.包含:

  • 使用基本的url相关路径来只做request
  • 为request自动添加设置http headers.
  • 使用http 基础证书或者OAuth来验证request
  • 为由client制作的requests管理一个NSOperationQueue
  • 从NSDictionary生成一个查询字符串或http bodies.
  • 从request中构建多部件
  • 自动的解析http response数据为相应的表现数据
  • 在网络可达性测试用监控和响应变化.

IMAGES

AFImageRequestOperation:一个AFHTTPRequestOperation的子类,用于下载和处理图片.

UIImageView+AFNetworking:添加一些方法到UIImageView中,为了从一个URL中异步加载远程图片

如何通过URL获取json数据

第一种,利用AFJSONRequestOperation,官方网站上给的例子:

[objc] view plaincopy

  1. NSString *str=[NSString stringWithFormat:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
  2. NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  3. NSURLRequest *request = [NSURLRequest requestWithURL:url];
  4. //    从URL获取json数据
  5. AFJSONRequestOperation *operation1 = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary* JSON) {
  6. NSLog(@"获取到的数据为:%@",JSON);
  7. } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id data) {
  8. NSLog(@"发生错误!%@",error);
  9. }];
  10. [operation1 start];

第二种方法,利用AFHTTPRequestOperation 先获取到字符串形式的数据,然后转换成json格式,将NSString格式的数据转换成json数据,利用IOS5自带的json解析方法:

[objc] view plaincopy

  1. NSString *str=[NSString stringWithFormat:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
  2. NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  3. NSURLRequest *request = [NSURLRequest requestWithURL:url];
  4. AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
  5. [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, idresponseObject) {
  6. NSString *html = operation.responseString;
  7. NSData* data=[html dataUsingEncoding:NSUTF8StringEncoding];
  8. id dict=[NSJSONSerialization  JSONObjectWithData:data options:0 error:nil];
  9. NSLog(@"获取到的数据为:%@",dict);
  10. }failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  11. NSLog(@"发生错误!%@",error);
  12. }];
  13. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  14. [queue addOperation:operation];

如果发生Error Domain=NSURLErrorDomain Code=-1000 "bad URL" UserInfo=0x14defc80 {NSUnderlyingError=0x14deea10 "bad URL", NSLocalizedDescription=bad URL这个错误,请检查URL编码格式。有没有进行stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding

如何通过URL获取图片

异步获取图片,通过队列实现,而且图片会有缓存,在下次请求相同的链接时,系统会自动调用缓存,而不从网上请求数据。

[objc] view plaincopy

  1. UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 100.0f, 100.0f, 100.0f)];      [imageView setImageWithURL:[NSURL URLWithString:@"http://i.imgur.com/r4uwx.jpg"]placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]];      [self.view addSubview:imageView];
  2. 上面的方法是官方提供的,还有一种方法,
  3. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.scott-sherwood.com/wp-content/uploads/2013/01/scene.png"]];
  4. AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse*response, UIImage *image) {
  5. self.backgroundImageView.image = image;
  6. } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
  7. NSLog(@"Error %@",error);
  8. }];
  9. [operation start];

如果使用第一种URLWithString:  placeholderImage:会有更多的细节处理,其实实现还是通过AFImageRequestOperation处理,可以点击URLWithString:  placeholderImage:方法进去看一下就一目了然了。所以我觉得还是用第一种好。

如何通过URL获取plist文件

通过url获取plist文件的内容,用的很少,这个方法在官方提供的方法里面没有

[objc] view plaincopy

  1. NSString *weatherUrl = @"http://www.calinks.com.cn/buick/kls/Buickhousekeeper.plist";
  2. NSURL *url = [NSURL URLWithString:[weatherUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  3. NSURLRequest *request = [NSURLRequest requestWithURL:url];
  4. [AFPropertyListRequestOperation addAcceptableContentTypes:[NSSet setWithObject:@"text/plain"]];
  5. AFPropertyListRequestOperation *operation = [AFPropertyListRequestOperation propertyListRequestOperationWithRequest:request success:^(NSURLRequest *request,NSHTTPURLResponse *response, id propertyList) {
  6. NSLog(@"%@",(NSDictionary *)propertyList);
  7. }failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, idpropertyList) {
  8. NSLog(@"%@",error);
  9. }];
  10. [operation start];

如何通过URL获取XML数据

xml解析使用AFXMLRequestOperation,需要实现苹果自带的NSXMLParserDelegate委托方法,XML中有一些不需要的协议格式内容,所以就不能像json那样解析,还得实现委托。我之前有想过能否所有的XML链接用一个类处理,而且跟服务端做了沟通,结果很不方便,效果不好。XML大多标签不同,格式也不固定,所以就有问题,使用json就要方便的多。

第一步;在.h文件中加入委托NSXMLParserDelegate

第二步;在.m文件方法中加入代码

[objc] view plaincopy

  1. NSURL *url = [NSURL URLWithString:@"http://113.106.90.22:5244/sshopinfo"];
  2. NSURLRequest *request = [NSURLRequest requestWithURL:url];
  3. AFXMLRequestOperation *operation =
  4. [AFXMLRequestOperation XMLParserRequestOperationWithRequest:request success:^(NSURLRequest*request, NSHTTPURLResponse *response, NSXMLParser *XMLParser) {
  5. XMLParser.delegate = self;
  6. [XMLParser setShouldProcessNamespaces:YES];
  7. [XMLParser parse];
  8. }failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser*XMLParser) {
  9. NSLog(@"%@",error);
  10. }];
  11. [operation start];

第三步;在.m文件中实现委托方法

//在文档开始的时候触发

-

[objc] view plaincopy

  1. (void)parserDidStartDocument:(NSXMLParser *)parser{
  2. NSLog(@"解析开始!");
  3. }
  4. //解析起始标记
  5. - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary*)attributeDict{
  6. NSLog(@"标记:%@",elementName);
  7. }
  8. //解析文本节点
  9. - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
  10. NSLog(@"值:%@",string);
  11. }
  12. //解析结束标记
  13. - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
  14. NSLog(@"结束标记:%@",elementName);
  15. }
  16. //文档结束时触发
  17. -(void) parserDidEndDocument:(NSXMLParser *)parser{
  18. NSLog(@"解析结束!");
  19. }

运行的结果:

如何使用AFHTTPClient进行web service操作

[objc] view plaincopy

  1. AFHTTPClient处理GET 和 POST请求.做网页的朋友们这个方法用的比较多。在要经常调用某个请求时,可以封装,节省资源。
  2. BaseURLString = @"http://www.raywenderlich.com/downloads/weather_sample/";
  3. NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:BaseURLString]];
  4. NSDictionary *parameters = [NSDictionary dictionaryWithObject:@"json" forKey:@"format"];
  5. AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
  6. [client registerHTTPOperationClass:[AFJSONRequestOperation class]];
  7. [client setDefaultHeader:@"Accept" value:@"text/html"];
  8. [client postPath:@"weather.php" parameters:parameters success:^(AFHTTPRequestOperation*operation, id responseObject) {
  9. NSString* newStr = [[NSString alloc] initWithData:responseObjectencoding:NSUTF8StringEncoding];
  10. NSLog(@"POST请求:%@",newStr);
  11. }failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  12. NSLog(@"%@",error);
  13. }];
  14. [client getPath:@"weather.php" parameters:parameters success:^(AFHTTPRequestOperation*operation, id responseObject) {
  15. NSString* newStr = [[NSString alloc] initWithData:responseObjectencoding:NSUTF8StringEncoding];
  16. NSLog(@"GET请求:%@",newStr);
  17. }failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  18. NSLog(@"%@",error);
  19. }];

运行结果:

如果需要显示网络活动指示器,可以用下面方法:

[objc] view plaincopy

  1. [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

FILE UPLOAD WITH PROGRESS CALLBACK

01

NSURL *url = [NSURL URLWithString:@"http://api-base-url.com"];

02

AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];

03

NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:@"avatar.jpg"], 0.5);

04

NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:@"POST" path:@"/upload" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {

05

[formData appendPartWithFileData:imageData name:@"avatar" fileName:@"avatar.jpg" mimeType:@"image/jpeg"];

06

}];

07

08

AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];

09

[operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {

10

NSLog(@"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);

11

}];

12

[operation start];

STREAMING REQUEST

1

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:8080/encode"]];

2

3

AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];

4

operation.inputStream = [NSInputStream inputStreamWithFileAtPath:[[NSBundle mainBundle] pathForResource:@"large-image" ofType:@"tiff"]];

5

operation.outputStream = [NSOutputStream outputStreamToMemory];

6

[operation start];

CoreData

1.  常用类和方法  ( 注意需要导入  coredata  框架  )

表结构:NSEntityDescription

表记录:NSManagedObject 数据库存放方式:NSPersistentStoreCoordinator(持久化存储协调者) 数据库操作:NSManagedObjectContext(被管理的对象上下文)

2. xcode  图形管理

2. 3.

4.

2. 操作数据库

一.连接数据库

  1. 首先需要创建一个操作数据库的上下文。NSManagedObjectContext
  2. 操作数据库的上下文需要设置一个调度者属性,这个调度者是用来将图形化 
    建立的模型和数据库联系起来。
  3. 给调度者添加一个需要联系的数据库。

二.添加,更新,删除

添加:

1. 新建实体 INST (插入) 2. 设置实体的属性

3. 保存上下文

更新:

1. 判断是否已有一模一样的模型 2. 设置实体属性

3. 保存上下文

删除

三.查询

1.使用 NSFetchedResultsController 控制器

2.1 当操作数据上下文的内容改变的时候,会自动调用抓取结果控制器的代理方 法

3.

注意:一定要执行抓取请求,返回的数据在 sections 里,这个数组中装的都是遵 守 NSFetchedResultsSectionInfo 这个协议的对象。通过

numberOfObjects 就能获取一组有多少数据对象了。

第一部分coredata的用法

先建立一个使用use coredata的工程,

在。xcdatamodeld文件中建立表格并为表格添加属性

为表格添加关系,

下一步生成表格model

其中生成的model:User和Department里面的属性用的是@dynamic

@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;

@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。

@dynamic告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var =someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

然后会再appdelegate里自动生成以下代码:

#pragma mark - Core Data stack

@synthesize managedObjectContext = _managedObjectContext;

@synthesize managedObjectModel = _managedObjectModel;

@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

//存储在沙盒里的具体位置

- (NSURL *)applicationDocumentsDirectory {

// The directory the application uses to store the Core Data store file. This code uses a directory named eims.CoreDatatest in the application's documents directory.

return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];

}

//托管对象

- (NSManagedObjectModel *)managedObjectModel {

// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.

if (_managedObjectModel != nil) {

return _managedObjectModel;

}

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@CoreDatatest withExtension:@momd];

_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

return _managedObjectModel;

}

//持久化存储协调器

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.

if (_persistentStoreCoordinator != nil) {

return _persistentStoreCoordinator;

}

// Create the coordinator and store

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@CoreDatatest.sqlite];

NSError *error = nil;

NSString *failureReason = @There was an error creating or loading the application's saved data.;

if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {

// Report any error we got.

NSMutableDictionary *dict = [NSMutableDictionary dictionary];

dict[NSLocalizedDescriptionKey] = @Failed to initialize the application's saved data;

dict[NSLocalizedFailureReasonErrorKey] = failureReason;

dict[NSUnderlyingErrorKey] = error;

error = [NSError errorWithDomain:@YOUR_ERROR_DOMAIN code:9999 userInfo:dict];

// Replace this with code to handle the error appropriately.

// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

NSLog(@Unresolved error %@, %@, error, [error userInfo]);

abort();

}

return _persistentStoreCoordinator;

}

//托管上下文

- (NSManagedObjectContext *)managedObjectContext {

// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)

if (_managedObjectContext != nil) {

return _managedObjectContext;

}

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

if (!coordinator) {

return nil;

}

_managedObjectContext = [[NSManagedObjectContext alloc] init];

[_managedObjectContext setPersistentStoreCoordinator:coordinator];

return _managedObjectContext;

}

#pragma mark - Core Data Saving support

- (void)saveContext {

NSManagedObjectContext *managedObjectContext = self.managedObjectContext;

if (managedObjectContext != nil) {

NSError *error = nil;

if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {

// Replace this implementation with code to handle the error appropriately.

// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

NSLog(@Unresolved error %@, %@, error, [error userInfo]);

abort();

}

}

}

这些代码知道具体作用就好,如果想自己手动建立起来coredata文件,也可以自己手动写

下面就是在viewcontroller的具体操作,

先引入appdelegate和User,Department的头文件

在viewcontroller里添加

@property (strong, nonatomic)AppDelegate *myAppDelegate;属性

然后,

具体操作,

添加:

User*user = (User*)[NSEntityDescription insertNewObjectForEntityForName:@User inManagedObjectContext:self.myAppDelegate.managedObjectContext];

[user setName:_nametextfield.text];

[user setAge:[NSNumber numberWithInteger:[_agetextfield.text integerValue]]];

[user setSex:_sextextfield.text];

NSError*error;

BOOL isSaveSuccess = [myAppDelegate.managedObjectContext save:&error];//保存(容易忘)

if (!isSaveSuccess) {

NSLog(@Error:%@,error);

_attentiontextview.text = [NSString stringWithFormat:@Error:%@,error];

}else{

NSLog(@Save Successful!);

_attentiontextview.text = @Save Successful!;

}

查询:

//数据请求(请求):命令集

NSFetchRequest*request = [[NSFetchRequest alloc]init];

//NSEntityDescription(实体描述):表

NSEntityDescription*user = [NSEntityDescription entityForName:@Department inManagedObjectContext:myAppDelegate.managedObjectContext];

[request setEntity:user];

NSError*error;

NSArray*mutablefetchResult = [myAppDelegate.managedObjectContext

executeFetchRequest:request error:&error];

if (mutablefetchResult == nil) {

NSLog(@Error: %@,mutablefetchResult);

}

NSLog(@the count of entry:%lu,[mutablefetchResult count]);

NSString*str = @;

for (Department*user in mutablefetchResult) {

// NSLog(@name:%@------age:%@-------sex:%@,user.name,user.age,user.sex);

// str = [str stringByAppendingFormat:@name:%@------age:%@-------sex:%@ ---depart:%@ ,user.name,user.age,user.sex,user.userrelationship.departmentname];

str = [str stringByAppendingFormat:@name:%@------ ,user.departmentname];

}

NSLog(@str:%@,str);

更新:

//NSFetchRequest 数据请求(请求):命令集

NSFetchRequest*request = [[NSFetchRequest alloc]init];

//NSEntityDescription(实体描述):表

NSEntityDescription*user = [NSEntityDescription entityForName:@User inManagedObjectContext:myAppDelegate.managedObjectContext];

[request setEntity:user];

//设置查询条件 NSPredicate (谓词):查询语句

NSPredicate*predicate = [NSPredicate predicateWithFormat:@name == %@,@lisi];

[request setPredicate:predicate];

NSError*error;

NSArray * mutablFetchResult = [myAppDelegate.managedObjectContext executeFetchRequest:request error:&error];

if (mutablFetchResult == nil) {

NSLog(@Error:%@,error);

_attentiontextview.text = [NSString stringWithFormat:@Error:%@,error];

}

NSLog(@the count of entry:%lu,[mutablFetchResult count]);

for (User*user in mutablFetchResult) {

[user setAge:[NSNumber numberWithInteger:999]];

}

//判断是否修改成功

BOOL isSaveSuccess = [myAppDelegate.managedObjectContext save:&error];//保存(容易忘)

if (!isSaveSuccess) {

NSLog(@Error:%@,error);

_attentiontextview.text = [NSString stringWithFormat:@Error:%@,error];

}else{

NSLog(@update Successful!);

_attentiontextview.text = @update Successful!;

}

删除:

//数据请求(命令集)

NSFetchRequest*request = [[NSFetchRequest alloc]init];

//实体描述(表)

NSEntityDescription*user = [NSEntityDescription entityForName:@Department inManagedObjectContext:myAppDelegate.managedObjectContext];

[request setEntity:user];

//设置查询条件

NSPredicate* predicate = [NSPredicate predicateWithFormat:@departmentname == %@,@公共事业部];

[request setPredicate:predicate];

NSError*error;

NSArray*mutableFetchResult = [myAppDelegate.managedObjectContext executeFetchRequest:request error:&error];

if (mutableFetchResult == nil) {

NSLog(@Error:%@,error);

_attentiontextview.text = [NSString stringWithFormat:@Error:%@,error];

}

NSLog(@mutableFetchResult %lu,[mutableFetchResult count]);

for (User*user in mutableFetchResult) {

[myAppDelegate.managedObjectContext deleteObject:user];

}

//判断是否删除成功

BOOL isDeleteSuccess = [myAppDelegate.managedObjectContext save:&error];//保存(容易忘)

if (!isDeleteSuccess) {

NSLog(@Error:%@,error);

_attentiontextview.text = [NSString stringWithFormat:@Error:%@,error];

}else{

NSLog(@delete Successful!);

_attentiontextview.text = @delete Successful!;

}

coredata并非严格的说是对sqlite数据库的一个封装,也可以用其他的数据库,并不一定要使用sqlite3,当然了coredata的好处还是非常多的,高效,简介,能节省至少50%的代码量,条目清新

ViewController.m

#import "ViewController.h"

#import <CoreData/CoreData.h>

#import "Person.h"

@interface ViewController ()

@property(nonatomic,retain) NSManagedObjectContext *context;

@end

@implementation ViewController

#pragma mark -生命周期方法

- (void)viewDidLoad

{

[super viewDidLoad];

[self LoadData];

//[self UdataData];

// [self addData];

//[self deleteData];

//  [self searchdata];

//面向对象操作

[self addPerson];

[self searchPerson];

// Do any additional setup after loading the view, typically from a nib.

}

- (void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

-(void)viewDidUnload{

[super viewDidUnload];

self.context=nil;

}

- (void)dealloc

{

[_context release];

[super dealloc];

}

#pragma mark-加载数据方式

-(void)LoadData{

//Nil表是从主bundles中加对应的模型实体

NSManagedObjectModel *model=[NSManagedObjectModel mergedModelFromBundles:nil];

for (NSEntityDescription *desc in model.entities) {

NSLog(@"%@",desc.name);

}

//通过模型 和数据库持久化

NSPersistentStoreCoordinator *storeCoordinator=[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

//持久化到coredata中

NSString *document= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

document=[document stringByAppendingPathComponent:@"coredata.db"];

NSURL *url=[NSURL fileURLWithPath:document];

NSError *error=nil;

[storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];

if(error){

NSLog(@"打开数据库失败");

return;

}

self.context=[[[NSManagedObjectContext alloc] init] autorelease];

self.context.persistentStoreCoordinator=storeCoordinator;

}

-(void)addData{

//把实体对象和实体上下文相关联

for (int i=0; i<10; i++) {

NSManagedObject *obj=[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.context];

NSString *name=[NSString stringWithFormat:@"gcb%i",i];

int age=i;

[obj setValue: name forKey:@"name"];

[obj setValue:@(age) forKey:@"age"];

//保存上下文中相关联的对象即可

}

[self.context save:nil];

}

-(void)searchdata{

NSFetchRequest *fetch=[NSFetchRequest fetchRequestWithEntityName:@"Person"];

//排序

NSSortDescriptor *sort=[NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];

fetch.sortDescriptors=@[sort];

//加入查询条件 age>20

//    fetch.predicate=[NSPredicate predicateWithFormat:@"age>%i",20];

//加入like *c1"

//fetch.predicate=[NSPredicate predicateWithFormat:@"name like %@",@"*cb1*"];

NSArray *arr=[self.context executeFetchRequest:fetch error:nil];

for (NSManagedObject *mode in arr) {

NSString *name=[mode valueForKey:@"name"];

int age =[[mode valueForKey:@"age"] intValue];

NSLog(@"%zi--%@",age,name);

}

}

//先查询出要修改的数据

-(void)UdataData{

//要操作那一张表

NSFetchRequest *Fetch=[NSFetchRequest fetchRequestWithEntityName:@"Person"];

//先创建排序描述,在排序

NSSortDescriptor *sort=[NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];

Fetch.sortDescriptors=@[sort];

//加入查询条件

Fetch.predicate=[NSPredicate predicateWithFormat:@"age>%i",5];

//把条件加入到上下文进行操作,得到查询集合

NSArray * arr=[self.context executeFetchRequest:Fetch error:nil];

for (NSManagedObject *obj in arr) {

//更改实体的数据

[obj setValue:@(50) forKey:@"age"];

}

//同步更数据库相关联的数据

[self.context save:nil];

}

//删除数据, 从数据库中取出来的对象,叫做NSManaedObject对象

-(void)deleteData{

//要找出上下文中操作的一张表

NSFetchRequest *FectchRequest=[NSFetchRequest fetchRequestWithEntityName:@"Person"];

FectchRequest.predicate=[NSPredicate predicateWithFormat:@"age<%i",50];

NSArray *arr=[self.context executeFetchRequest:FectchRequest error:nil];

for (NSManagedObject *obj in arr) {

[self.context deleteObject:obj];

}

//同步数据库

[self.context save:nil];

}

#pragma mark-面向对象开发

-(void)addPerson{

//把要插入的实体和当前上下文相关联

Person *ps=[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.context];

ps.name=@"mj__itcast";

ps.age=@28;

//同步数据和上下文相关联的

[self.context save:nil];

}

-(void)searchPerson{

NSFetchRequest *FetchRequest=[NSFetchRequest fetchRequestWithEntityName:@"Person"];

NSArray *arr=[self.context executeFetchRequest:FetchRequest error:nil];

for (Person *ps in arr) {

NSLog(@"name=%@,age=%@",ps.name,ps.age);

}

}

@end

1 多线程是什么

多线程是个复杂的概念,按字面意思是同步完成多项任务,提高了资源的使用效率,从硬件、操作系统、应用软件不同的角度去看,多线程被赋予不同的内涵,对于硬件,现在市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;从操作系统角度,是多任务,现在用的主流操作系统都是多任务的,可以一边听歌、一边写博客;对于应用来说,多线程可以让应用有更快的回应,可以在网络下载时,同时响应用户的触摸操作。在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先做烧水,再摘菜,再炒菜的工作,会变成烧水的同时去摘菜,最后去炒菜。

2 iOS 中的多线程

iOS中的多线程,是Cocoa框架下的多线程,通过Cocoa的封装,可以让我们更为方便的使用线程,做过C++的同学可能会对线程有更多的理解,比如线程的创立,信号量、共享变量有认识,Cocoa框架下会方便很多,它对线程做了封装,有些封装,可以让我们创建的对象,本身便拥有线程,也就是线程的对象化抽象,从而减少我们的工程,提供程序的健壮性。

GCD是(Grand Central Dispatch)的缩写 ,从系统级别提供的一个易用地多线程类库,具有运行时的特点,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为我们提供强大的“接口”,对于GCD的使用参见本文

NSOperation与Queue

NSOperation是一个抽象类,它封装了线程的细节实现,我们可以通过子类化该对象,加上NSQueue来同面向对象的思维,管理多线程程序。具体可参看这里:一个基于NSOperation的多线程网络访问的项目。

NSThread

NSThread是一个控制线程执行的对象,它不如NSOperation抽象,通过它我们可以方便的得到一个线程,并控制它。但NSThread的线程之间的并发控制,是需要我们自己来控制的,可以通过NSCondition实现。

参看

iOS多线程编程之NSThread的使用

其他多线程

在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程,(待补充).

3 iOS多线程常见面试题

在项目什么时候选择使用GCD,什么时候选择NSOperation?

项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。

项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。

使用GCD处理多线程,在多核心CPU下,会提高执行效率,下面是一段在项目中使用的GCD代码。

[cpp] view plaincopyprint?

  1. - (void)gcdDownload
  2. {
  3. static dispatch_once_t once;
  4. static dispatch_queue_t queue;
  5. //create download queue
  6. dispatch_once(&once, ^{
  7. queue =dispatch_queue_create("com.xxx.download.background",DISPATCH_QUEUE_CONCURRENT);
  8. });
  9. //__block type
  10. __block BOOL downloadFlag = NO;
  11. dispatch_async(queue, ^{
  12. //        downloadFlag = [Download sendRequest:request];
  13. NSLog(@"长时间任务,如网络下载");
  14. });
  15. dispatch_barrier_async(queue,^{
  16. if (downloadFlag) {
  17. NSLog(@"下载成功完成");
  18. }
  19. dispatch_async(dispatch_get_main_queue(), ^{
  20. NSLog(@"执行完下载,回掉回主线程,例如刷新UI");
  21. });
  22. });
  23. }

可以向queue里放多个并发block。

Grand Central Dispatch (GCD) 是 Apple 开发的一个多核编程的解决方法。该方法在 Mac OS X 10.6 雪豹中首次推出,并随后被引入到了 iOS4.0 中。GCD 是一个替代诸如 NSThread, NSOperationQueue, NSInvocationOperation 等技术的很高效和强大的技术。

GCD 和 block 的配合使用,可以方便地进行多线程编程。

应用举例

让我们来看一个编程场景。我们要在 iPhone 上做一个下载网页的功能,该功能非常简单,就是在 iPhone 上放置一个按钮,点击该按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成之后,将内容加载到界面上的一个文本控件中。

不用 GCD 前

虽然功能简单,但是我们必须把下载过程放到后台线程中,否则会阻塞 UI 线程显示。所以,如果不用 GCD, 我们需要写如下 3 个方法:

  • someClick 方法是点击按钮后的代码,可以看到我们用 NSInvocationOperation 建了一个后台线程,并且放到 NSOperationQueue 中。后台线程执行 download 方法。
  • download 方法处理下载网页的逻辑。下载完成后用 performSelectorOnMainThread 执行 download_completed 方法。
  • download_completed 进行 clear up 的工作,并把下载的内容显示到文本控件中。

这 3 个方法的代码如下。可以看到,虽然 开始下载 –> 下载中 –> 下载完成 这 3 个步骤是整个功能的三步。但是它们却被切分成了 3 块。他们之间因为是 3 个方法,所以还需要传递数据参数。如果是复杂的应用,数据参数很可能就不象本例子中的 NSString 那么简单了,另外,下载可能放到 Model 的类中来做,而界面的控制放到 View Controller 层来做,这使得本来就分开的代码变得更加散落。代码的可读性大大降低。

使用 GCD 后

如果使用 GCD,以上 3 个方法都可以放到一起,如下所示:

// 原代码块一

self.indicator.hidden = NO;

[self.indicator startAnimating];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 原代码块二

NSURL * url = [NSURL URLWithString:@"http://www.youdao.com"];

NSError * error;

NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];

if (data != nil) {

// 原代码块三

dispatch_async(dispatch_get_main_queue(), ^{

[self.indicator stopAnimating];

self.indicator.hidden = YES;

self.content.text = data;

});

} else {

NSLog(@"error when download:%@", error);

}

});

首先我们可以看到,代码变短了。因为少了原来 3 个方法的定义,也少了相互之间需要传递的变量的封装。

另外,代码变清楚了,虽然是异步的代码,但是它们被 GCD 合理的整合在一起,逻辑非常清晰。如果应用上 MVC 模式,我们也可以将 View Controller 层的回调函数用 GCD 的方式传递给 Modal 层,这相比以前用 @selector 的方式,代码的逻辑关系会更加清楚。

block 的定义

block 的定义有点象函数指针,差别是用 ^ 替代了函数指针的 * 号,如下所示:

1

2

3

4

5

6

7

8

9

// 申明变量

(void) (^loggerBlock)(void);

// 定义

loggerBlock = ^{

NSLog(@"Hello world");

};

// 调用

loggerBlock();

但是大多数时候,我们通常使用内联的方式来定义 block,即将它的程序块写在调用的函数里面,例如这样:

1

2

3

dispatch_async(dispatch_get_global_queue(0, 0), ^{

// something

});

从上面大家可以看出,block 有如下特点:

  1. 程序块可以在代码中以内联的方式来定义。
  2. 程序块可以访问在创建它的范围内的可用的变量。

系统提供的 dispatch 方法

为了方便地使用 GCD,苹果提供了一些方法方便我们将 block 放在主线程 或 后台线程执行,或者延后执行。使用的例子如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

//  后台执行:

dispatch_async(dispatch_get_global_queue(0, 0), ^{

// something

});

// 主线程执行:

dispatch_async(dispatch_get_main_queue(), ^{

// something

});

// 一次性执行:

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

// code to be executed once

});

// 延迟 2 秒执行:

double delayInSeconds = 2.0;

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

// code to be executed on the main queue after delay

});

dispatch_queue_t 也可以自己定义,如要要自定义 queue,可以用 dispatch_queue_create 方法,示例如下:

1

2

3

4

5

dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);

dispatch_async(urls_queue, ^{

// your code

});

dispatch_release(urls_queue);

另外,GCD 还有一些高级用法,例如让后台 2 个线程并行执行,然后等 2 个线程都结束后,再汇总执行结果。这个可以用 dispatch_group, dispatch_group_async 和 dispatch_group_notify 来实现,示例如下:

1

2

3

4

5

6

7

8

9

10

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{

// 并行执行的线程一

});

dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{

// 并行执行的线程二

});

dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{

// 汇总结果

});

修改 block 之外的变量

默认情况下,在程序块中访问的外部变量是复制过去的,即写操作不对原变量生效。但是你可以加上 __block 来让其写操作生效,示例代码如下:

1

2

3

4

5

6

__block int a = 0;

void  (^foo)(void) = ^{

a = 1;

}

foo();

// 这里,a 的值被修改为 1

后台运行

使用 block 的另一个用处是可以让程序在后台较长久的运行。在以前,当 app 被按 home 键退出后,app 仅有最多 5 秒钟的时候做一些保存或清理资源的工作。但是应用可以调用 UIApplication 的beginBackgroundTaskWithExpirationHandler方法,让 app 最多有 10 分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

让程序在后台长久运行的示例代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

// AppDelegate.h 文件

@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate.m 文件

- (void)applicationDidEnterBackground:(UIApplication *)application

{

[self beingBackgroundUpdateTask];

// 在这里加上你需要长久运行的代码

[self endBackgroundUpdateTask];

}

- (void)beingBackgroundUpdateTask

{

self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

[self endBackgroundUpdateTask];

}];

}

- (void)endBackgroundUpdateTask

{

[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];

self.backgroundUpdateTask = UIBackgroundTaskInvalid;

}

总结

总体来说,GCD 能够极大地方便开发者进行多线程编程。大家应该尽量使用 GCD 来处理后台线程和 UI 线程的交互。

1.1 iOS有三种多线程编程的技术,分别是:

1.、NSThread

2、Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的使用

3、GCD  全称:Grand Central Dispatch( iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用

这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。

这篇我们主要介绍和使用NSThread,后面会继续2、3 的讲解和使用。

1.2 三种方式的有缺点介绍:

NSThread:

优点:NSThread 比其他两个轻量级

缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

NSThread实现的技术有下面三种:

一般使用cocoa thread 技术。

Cocoa operation

优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。

Cocoa operation 相关的类是 NSOperation ,NSOperationQueue。NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。

GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到6了,所以不用担心该技术不能使用。

介绍完这三种多线程编程方式,我们这篇先介绍NSThread的使用。

2、NSThread的使用

2.1 NSThread 有两种直接创建方式:

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument

+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument

第一个是实例方法,第二个是类方法

[cpp] view plaincopy

  1. 1、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
  2. 2、NSThread* myThread = [[NSThread alloc] initWithTarget:self
  3. selector:@selector(doSomething:)
  4. object:nil];
  5. [myThread start];

2.2参数的意义:

selector :线程执行的方法,这个selector只能有一个参数,而且不能有返回值。

target  :selector消息发送的对象

argument:传输给target的唯一参数,也可以是nil

第一种方式会直接创建线程并且开始运行线程,第二种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息

2.3 PS:不显式创建线程的方法:

用NSObject的类方法  performSelectorInBackground:withObject: 创建一个线程:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];

2.4 下载图片的例子:

2.4.1  新建singeView app

新建项目,并在xib文件上放置一个imageView控件。按住control键拖到viewControll

er.h文件中创建imageView IBOutlet

ViewController.m中实现:

[cpp] view plaincopy

  1. //
  2. //  ViewController.m
  3. //  NSThreadDemo
  4. //
  5. //  Created by rongfzh on 12-9-23.
  6. //  Copyright (c) 2012年 rongfzh. All rights reserved.
  7. //
  8. #import "ViewController.h"
  9. #define kURL @"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"
  10. @interface ViewController ()
  11. @end
  12. @implementation ViewController
  13. -(void)downloadImage:(NSString *) url{
  14. NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
  15. UIImage *image = [[UIImage alloc]initWithData:data];
  16. if(image == nil){
  17. }else{
  18. [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
  19. }
  20. }
  21. -(void)updateUI:(UIImage*) image{
  22. self.imageView.image = image;
  23. }
  24. - (void)viewDidLoad
  25. {
  26. [super viewDidLoad];
  27. //    [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];
  28. NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];
  29. [thread start];
  30. }
  31. - (void)didReceiveMemoryWarning
  32. {
  33. [super didReceiveMemoryWarning];
  34. // Dispose of any resources that can be recreated.
  35. }
  36. @end

2.4.2线程间通讯

线程下载完图片后怎么通知主线程更新界面呢?

[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];

performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的比如:

用:performSelector:onThread:withObject:waitUntilDone:

运行下载图片:

图片下载下来了。

2.3 线程同步

我们演示一个经典的卖票的例子来讲NSThread的线程同步:

.h

[cpp] view plaincopy

  1. #import <UIKit/UIKit.h>
  2. @class ViewController;
  3. @interface AppDelegate : UIResponder <UIApplicationDelegate>
  4. {
  5. int tickets;
  6. int count;
  7. NSThread* ticketsThreadone;
  8. NSThread* ticketsThreadtwo;
  9. NSCondition* ticketsCondition;
  10. NSLock *theLock;
  11. }
  12. @property (strong, nonatomic) UIWindow *window;
  13. @property (strong, nonatomic) ViewController *viewController;
  14. @end

[cpp] view plaincopy

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  2. {
  3. tickets = 100;
  4. count = 0;
  5. theLock = [[NSLock alloc] init];
  6. // 锁对象
  7. ticketsCondition = [[NSCondition alloc] init];
  8. ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
  9. [ticketsThreadone setName:@"Thread-1"];
  10. [ticketsThreadone start];
  11. ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
  12. [ticketsThreadtwo setName:@"Thread-2"];
  13. [ticketsThreadtwo start];
  14. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  15. // Override point for customization after application launch.
  16. self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
  17. self.window.rootViewController = self.viewController;
  18. [self.window makeKeyAndVisible];
  19. return YES;
  20. }
  21. - (void)run{
  22. while (TRUE) {
  23. // 上锁
  24. //        [ticketsCondition lock];
  25. [theLock lock];
  26. if(tickets >= 0){
  27. [NSThread sleepForTimeInterval:0.09];
  28. count = 100 - tickets;
  29. NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);
  30. tickets--;
  31. }else{
  32. break;
  33. }
  34. [theLock unlock];
  35. //        [ticketsCondition unlock];
  36. }
  37. }

如果没有线程同步的lock,卖票数可能是-1.加上lock之后线程同步保证了数据的正确性。
上面例子我使用了两种锁,一种NSCondition ,一种是:NSLock。 NSCondition我已经注释了。

线程的顺序执行

他们都可以通过

[ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。

比如:

[cpp] view plaincopy

  1. #import "AppDelegate.h"
  2. #import "ViewController.h"
  3. @implementation AppDelegate
  4. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  5. {
  6. tickets = 100;
  7. count = 0;
  8. theLock = [[NSLock alloc] init];
  9. // 锁对象
  10. ticketsCondition = [[NSCondition alloc] init];
  11. ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
  12. [ticketsThreadone setName:@"Thread-1"];
  13. [ticketsThreadone start];
  14. ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
  15. [ticketsThreadtwo setName:@"Thread-2"];
  16. [ticketsThreadtwo start];
  17. NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];
  18. [ticketsThreadthree setName:@"Thread-3"];
  19. [ticketsThreadthree start];
  20. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  21. // Override point for customization after application launch.
  22. self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
  23. self.window.rootViewController = self.viewController;
  24. [self.window makeKeyAndVisible];
  25. return YES;
  26. }
  27. -(void)run3{
  28. while (YES) {
  29. [ticketsCondition lock];
  30. [NSThread sleepForTimeInterval:3];
  31. [ticketsCondition signal];
  32. [ticketsCondition unlock];
  33. }
  34. }
  35. - (void)run{
  36. while (TRUE) {
  37. // 上锁
  38. [ticketsCondition lock];
  39. [ticketsCondition wait];
  40. [theLock lock];
  41. if(tickets >= 0){
  42. [NSThread sleepForTimeInterval:0.09];
  43. count = 100 - tickets;
  44. NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);
  45. tickets--;
  46. }else{
  47. break;
  48. }
  49. [theLock unlock];
  50. [ticketsCondition unlock];
  51. }
  52. }

wait是等待,我加了一个 线程3 去唤醒其他两个线程锁中的wait

其他同步

我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
- (void)doSomeThing:(id)anObj
{
    @synchronized(anObj)
    {
        // Everything between the braces is protected by the @synchronized directive.
    }
}
还有其他的一些锁对象,比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等

1 什么是block

对于闭包(block),有很多定义,其中闭包就是能够读取其它函数内部变量的函数,这个定义即接近本质又较好理解。对于刚接触Block的同学,会觉得有些绕,因为我们习惯写这样的程序main(){ funA();} funA(){funB();} funB(){.....}; 就是函数main调用函数A,函数A调用函数B... 函数们依次顺序执行,但现实中不全是这样的,例如项目经理M,手下有3个程序员A、B、C,当他给程序员A安排实现功能F1时,他并不等着A完成之后,再去安排B去实现F2,而是安排给A功能F1,B功能F2,C功能F3,然后可能去写技术文档,而当A遇到问题时,他会来找项目经理M,当B做完时,会通知M,这就是一个异步执行的例子。在这种情形下,Block便可大显身手,因为在项目经理M,给A安排工作时,同时会告诉A若果遇到困难,如何能找到他报告问题(例如打他手机号),这就是项目经理M给A的一个回调接口,要回掉的操作,比如接到电话,百度查询后,返回网页内容给A,这就是一个Block,在M交待工作时,已经定义好,并且取得了F1的任务号(局部变量),却是在当A遇到问题时,才调用执行,跨函数在项目经理M查询百度,获得结果后回调该block。

2 block 实现原理

Objective-C是对C语言的扩展,block的实现是基于指针和函数指针。

从计算语言的发展,最早的goto,高级语言的指针,到面向对象语言的block,从机器的思维,一步步接近人的思维,以方便开发人员更为高效、直接的描述出现实的逻辑(需求)。

下面是两篇很好的介绍block实现的博文

iOS中block实现的探究

谈Objective-C Block的实现

3 block的使用

使用实例

cocoaTouch框架下动画效果的Block的调用

使用typed声明block

typedef void(^didFinishBlock) (NSObject *ob);

这就声明了一个didFinishBlock类型的block,

然后便可用

@property (nonatomic,copy) didFinishBlock  finishBlock;

声明一个block对象,注意对象属性设置为copy,接到block 参数时,便会自动复制一份。

__block是一种特殊类型,

使用该关键字声明的局部变量,可以被block所改变,并且其在原函数中的值会被改变。

4 常见系列面试题

面试时,面试官会先问一些,是否了解block,是否使用过block,这些问题相当于开场白,往往是下面一系列问题的开始,所以一定要如实根据自己的情况回答。

1 使用block和使用delegate完成委托模式有什么优点?

首先要了解什么是委托模式,委托模式在iOS中大量应用,其在设计模式中是适配器模式中的对象适配器,Objective-C中使用id类型指向一切对象,使委托模式更为简洁。了解委托模式的细节:

iOS设计模式----委托模式

使用block实现委托模式,其优点是回调的block代码块定义在委托对象函数内部,使代码更为紧凑;

适配对象不再需要实现具体某个protocol,代码更为简洁。

2 多线程与block

GCD与Block

使用 dispatch_async 系列方法,可以以指定的方式执行block

GCD编程实例

dispatch_async的完整定义

void dispatch_async(

dispatch_queue_t queue,

dispatch_block_t block);

功能:在指定的队列里提交一个异步执行的block,不阻塞当前线程

通过queue来控制block执行的线程。主线程执行前文定义的 finishBlock对象

dispatch_async(dispatch_get_main_queue(),^(void){finishBlock();});

1 谈谈对Block 的理解?并写出一个使用Block执行UIVew动画?

答案:Block是可以获取其他函数局部变量的匿名函数,其不但方便开发,并且可以大幅提高应用的执行效率(多核心CPU可直接处理Block指令)

[cpp] view plaincopyprint?

  1. [UIView transitionWithView:self.view
  2. duration:0.2
  3. options:UIViewAnimationOptionTransitionFlipFromLeft
  4. animations:^{ [[blueViewController view] removeFromSuperview]; [[self view] insertSubview:yellowViewController.view atIndex:0]; }
  5. completion:NULL];

2 写出上面代码的Block的定义。

答案:

typedef void(^animations) (void);

typedef void(^completion) (BOOL finished);

3 试着使用+ beginAnimations:context:以及上述Block的定义,写出一个可以完成

+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);操作的函数执行部分

答案:无

网络部分

3 做过的项目是否涉及网络访问功能,使用什么对象完成网络功能?

答案:ASIHTTPRequest与NSURLConnection

4 简单介绍下NSURLConnection类及+ sendSynchronousRequest:returningResponse:error:– initWithRequest:delegate:两个方法的区别?

答案:  NSURLConnection主要用于网络访问,其中+ sendSynchronousRequest:returningResponse:error:是同步访问数据,即当前线程会阻塞,并等待request的返回的response,而– initWithRequest:delegate:使用的是异步加载,当其完成网络访问后,会通过delegate回到主线程,并其委托的对象。

动画效果是IOS界面重要的特色之一,其中CAAnimation是所有动画对象的抽象父类,作为新人,使用较多的是UIView下的动画方法(类方法)。使用UIView下的动画,有下面几个方法。

方法一:设置beginAnimations

其中memberView为需要添加的子视图的视图,mivc.view为子视图,在使用的时候,需要将这两个地方替换

[cpp] view plaincopyprint?

  1. [UIView beginAnimations:@"view flip" context:nil];
  2. [UIView setAnimationDuration:1];
  3. [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:memberView cache:YES];
  4. [memberView addSubview:mivc.view];
  5. [UIView commitAnimations];

需要注意的是,一定要使用[UIView commitAnimations];动画才会生效

通过[UIView setAnimationDuration:1]; 设置持续时间。

在IOS4.0后,我们有了新的方法,+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion,依然是UIView的类方法,但使用到了Block对象,Block对象是一组指令,可以传递(像变量一样),可以把它想像成C语言的函数指针。

方法二:

其中在当前视图下,删除[blueViewController view],添加[yellowViewController view],在使用时,这两个地方要替换

[cpp] view plaincopyprint?

  1. [UIView transitionWithView:self.view
  2. duration:0.2
  3. options:UIViewAnimationOptionTransitionFlipFromLeft
  4. animations:^{ [[blueViewController view] removeFromSuperview]; [[self view] insertSubview:yellowViewController.view atIndex:0]; }
  5. completion:NULL];

其中animations:后的block为对特定视图发生的改变,其不能为是NULL,completion:后block为动画执行完后,需要执行的代码块,可以为NULL。

根据手册,在整个动画过程中,用户交互对于此视图是暂时无效的(而IOS5.0前,动画过程中,用户交互对于整个应用是无效的),如果想要用户可以和视图交互,可以改变 UIViewAnimationOptionAllowUserInteraction 的值。

Cocoa Touch框架下使用大量使用委托(delegation),根据苹果公司的文档,delegate是Cocoa Touch框架所采用的委托模式的一种形式。实例代码下载传送门

理解委托,所需要的准备

(一)协议

Objective-C的协议,类似于C++的抽象类,JAVA的接口。其具体定义如下

[cpp] view plaincopyprint?

  1. @protocol MyButtonDelegate <NSObject>
  2. @optional
  3. - (void) didPressButton;
  4. @end

@protocol为协议关键字,MyButtonDelegate为协议名,didPressButton为协议里的方法。

(二)id类型

id类型可以理解为可以指向任意对象的指针,

其定义为:

[cpp] view plaincopyprint?

  1. typedef struct objc_class *Class;
  2. typedef struct objc_object {
  3. Class isa;
  4. } *id;

(三)适配器模式

在设计模式中,并没有委托模式。但有适配器模式,适配器模式可以这样来理解,假如我们去了香港,要给我电脑接电源,发现有些插板怎么也插不上(香港使用的是英式插头),只能先插个转换器,在把电脑接到转换器上。这就是生活中的适配器模式,大多数委托模式,就是实现的对象适配器的功能,

(四)实例

我们想实现一个自己构建的类似于UIButton功能的组件。首先,从UIView继承下来Mybutton,这时候遇到一个问题,我们不知道未来谁会用到Mybutton,但我们知道每个用到mybutton的,都需要在这个按钮被按下时,获得一个消息,来告诉用到mybutton的对象(Adaptee),mybuttton被按下。

这时候我们的适配的<Target>如下:

[cpp] view plaincopyprint?

  1. @protocol MyButtonDelegate <NSObject>
  2. @optional
  3. - (void) didPressButton;
  4. @end

我的Adapter是Mybutton,其通过

[cpp] view plaincopyprint?

  1. @property (nonatomic,assign) id <MyButtonDelegate>delegate;

[cpp] view plaincopyprint?

  1. if (delegate && [delegate respondsToSelector:@selector(didPressButton)]) {
  2. [delegate performSelector:@selector(didPressButton)];
  3. }

来实现对Adaptee的调用,其中Adaptee可以是任意对象,在这个例子中,是RootViewController(实现了<MyButtonDelegate>协议)

(五)委托模式的深入理解

委托模式的实现,也可以通过Block来实现,但仅适合一次性回调执行的代码。

1.When to use NSMutableArray and when to use NSArray?

什么时候使用NSMutableArray,什么时候使用NSArray?

答案:当数组在程序运行时,需要不断变化的,使用NSMutableArray,当数组在初始化后,便不再改变的,使用NSArray。需要指出的是,使用NSArray只表明的是该数组在运行时不发生改变,即不能往NSAarry的数组里新增和删除元素,但不表明其数组內的元素的内容不能发生改变。NSArray是线程安全的,NSMutableArray不是线程安全的,多线程使用到NSMutableArray需要注意。

2.Give us example of what are delegate methods and what are data source methods of uitableview.

给出委托方法的实例,并且说出UITableVIew的Data Source方法

答案:CocoaTouch框架中用到了大量委托,其中UITableViewDelegate就是委托机制的典型应用,是一个典型的使用委托来实现适配器模式,其中UITableViewDelegate协议是目标,tableview是适配器,实现UITableViewDelegate协议,并将自身设置为talbeview的delegate的对象,是被适配器,一般情况下该对象是UITableViewController。

UITableVIew的Data Source方法有- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

3.How many autorelease you can create in your application? Is there any limit?

在应用中可以创建多少autorelease对象,是否有限制?

答案:无

4.If we don’t create any autorelease pool in our application then is there any autorelease pool already provided to us?

如果我们不创建内存池,是否有内存池提供给我们?

答案:界面线程维护着自己的内存池,用户自己创建的数据线程,则需要创建该线程的内存池

5.When you will create an autorelease pool in your application?

什么时候需要在程序中创建内存池?

答案:用户自己创建的数据线程,则需要创建该线程的内存池

6.When retain count increase?

什么时候内存计数会增加?

答案:见iOS面试题(一)

7.What are commonly used NSObject class methods?

类NSObject的那些方法经常被使用?

答案:NSObject是Objetive-C的基类,其由NSObject类及一系列协议构成。

其中类方法alloc、class、 description 对象方法init、dealloc、– performSelector:withObject:afterDelay:等经常被使用

8.What is convenience constructor?

什么是简便构造方法?

答案:简便构造方法一般由CocoaTouch框架提供,如NSNumber的 + numberWithBool:  + numberWithChar:  + numberWithDouble:  + numberWithFloat:  + numberWithInt:

Foundation下大部分类均有简便构造方法,我们可以通过简便构造方法,获得系统给我们创建好的对象,并且不需要手动释放。

9.How to design universal application in Xcode?

如何使用Xcode设计通用应用?

答案:使用MVC模式设计应用,其中Model层完成脱离界面,即在Model层,其是可运行在任何设备上,在controller层,根据iPhone与iPad(独有UISplitViewController)的不同特点选择不同的viewController对象。在View层,可根据现实要求,来设计,其中以xib文件设计时,其设置其为universal。

10.What is keyword atomic in Objective C?

在Objetive-C什么时原子关键字

答案:atomic,nonatomic见iOS面试题(一)

11.What are UIView animations?

UIView的动画效果有那些?

答案:有很多,如  UIViewAnimationOptionCurveEaseInOut     UIViewAnimationOptionCurveEaseIn     UIViewAnimationOptionCurveEaseOut    UIViewAnimationOptionTransitionFlipFromLeft   UIViewAnimationOptionTransitionFlipFromRight     UIViewAnimationOptionTransitionCurlUpUIViewAnimationOptionTransitionCurlDown

如何使用可见该博文

12.How can you store data in iPhone applications?

在iPhone应用中如何保存数据?

答案:有以下几种保存机制:

1.通过web服务,保存在服务器上

2.通过NSCoder固化机制,将对象保存在文件中

3.通过SQlite或CoreData保存在文件数据库中

13.What is coredata?

什么是coredata?

答案:coredata框架是apple提供的一套通用自动的解决方案,包括了对象生存周期、对象关系图、持久化机制。

补充答案:上面是翻译的,按我个人理解coredata提供一种一机制,让我们可以方便的把内存中对象,及对象间的关系,映射到coredata,然后由它为我们持久化数据。相比普通的文件数据库SQlite,它的功能更强大,不需要我们先将对象数据format成SQL语句,存入数据库,再用select语句读出,而现在是从内存到coredata的数据管理,我们只需管理coredata的managed对象。

是苹果提供一套数据保存

14.What is NSManagedObject model?

什么是NSManagedObject模型?

答案:NSManagedObject是NSObject的子类 ,也是coredata的重要组成部分,它是一个通用的类,实现了core data 模型层所需的基本功能,用户可通过子类化NSManagedObject,建立自己的数据模型。

15.What is NSManagedobjectContext?

什么是NSManagedobjectContext?

答案:NSManagedobjectContext对象负责应用和数据库之间的交互。

16.What is predicate?

什么是谓词?

答案:谓词是通过NSPredicate,是通过给定的逻辑条件作为约束条件,完成对数据的筛选。

predicate = [NSPredicate predicateWithFormat:@"customerID == %d",n];

a = [customers filteredArrayUsingPredicate:predicate];

17.What kind of persistence store we can use with coredata?

coredata有哪几种持久化存储机制?

答案:coredatat提供以下几种存储机制:XML(iOS系统不支持),自动存储,SQLite,内存存储。

补充说明:这个问题问的是,coredate框架的存储机制,平时使用coredata时,更多关注的是managed的对象,这里是coerdata框架的存储实现细节。BTW: 其他常用的持久化存储方法 :存入到文件、 存入到NSUserDefaults(系统plist文件中)。

1.Difference between shallow copy and deep copy?
浅复制和深复制的区别?
答案:浅层复制:只复制指向对象的指针,而不复制引用对象本身。
深层复制:复制引用对象本身。
意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源
还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了
两份独立对象本身。

2.What is advantage of categories? What is difference between implementing a category and inheritance?
类别的作用?继承和类别在实现中有何区别?
答案:category 可以在不获悉,不改变原来代码的情况下往里面添加新的方法,只能添加,不能删除修改。
并且如果类别和原来类中的方法产生名称冲突,则类别将覆盖原来的方法,因为类别具有更高的优先级。
类别主要有3个作用:
(1)将类的实现分散到多个不同文件或多个不同框架中。
(2)创建对私有方法的前向引用。
(3)向对象添加非正式协议。
 继承可以增加,扩展父类方法,并且可以增加属性。

3.Difference between categories and extensions?
类别和类扩展的区别。
 答案:category和extensions的不同在于 后者可以添加属性。另外后者添加的方法是必须要实现的。
extensions可以认为是一个私有的Category。

4.Difference between protocol in objective c and interfaces in java?
oc中的协议和java中的接口概念有何不同?
答案:OC中的协议有2层含义,官方定义为 formal和informal protocol。前者和Java接口一样。
informal protocol中的方法属于设计模式考虑范畴,不是必须实现的,但是如果有实现,就会改变类的属性。
其实关于正式协议,类别和非正式协议我很早前学习的时候大致看过,也写在了学习教程里
“非正式协议概念其实就是类别的另一种表达方式“这里有一些你可能希望实现的方法,你可以使用他们更好的完成工作”。
这个意思是,这些是可选的。比如我门要一个更好的方法,我们就会申明一个这样的类别去实现。然后你在后期可以直接使用这些更好的方法。
这么看,总觉得类别这玩意儿有点像协议的可选协议。"
现在来看,其实protocal已经开始对两者都统一和规范起来操作,因为资料中说“非正式协议使用interface修饰“,
现在我们看到协议中两个修饰词:“必须实现(@requied)”和“可选实现(@optional)”。

OC中的协议(formal protocol)与java中的接口概念基本一致,OC中非正式协议(informal protocol)就是类别。在java中如果继承了接口,但不实现其方法,会得到一个error(无法编译);在OC中的正式协议,如果不实现,会得到一个warning(可编译执行),如果想去除waring,还可以加关键字(@optional),让它可选实现方法。

5.What are KVO and KVC?
答案:kvc:键 - 值编码是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制。
很多情况下可以简化程序代码。apple文档其实给了一个很好的例子。
kvo:键值观察机制,他提供了观察某一属性变化的方法,极大的简化了代码。
具体用看到嗯哼用到过的一个地方是对于按钮点击变化状态的的监控。
比如我自定义的一个button
[cpp] 
[self addObserver:self forKeyPath:@"highlighted" options:0 context:nil]; 
 
 
#pragma mark KVO 
 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 

    if ([keyPath isEqualToString:@"highlighted"] ) { 
        [self setNeedsDisplay]; 
    } 
}

对于系统是根据keypath去取的到相应的值发生改变,理论上来说是和kvc机制的道理是一样的。
对于kvc机制如何通过key寻找到value:
“当通过KVC调用对象时,比如:[self valueForKey:@”someKey”]时,程序会自动试图通过几种不同的方式解析这个调用。首先查找对象是否带有 someKey 这个方法,如果没找到,会继续查找对象是否带有someKey这个实例变量(iVar),如果还没有找到,程序会继续试图调用 -(id) valueForUndefinedKey:这个方法。如果这个方法还是没有被实现的话,程序会抛出一个NSUndefinedKeyException异常错误。
 
(cocoachina.com注:Key-Value Coding查找方法的时候,不仅仅会查找someKey这个方法,还会查找getsomeKey这个方法,前面加一个get,或者_someKey以及_getsomeKey这几种形式。同时,查找实例变量的时候也会不仅仅查找someKey这个变量,也会查找_someKey这个变量是否存在。)
 
设计valueForUndefinedKey:方法的主要目的是当你使用-(id)valueForKey方法从对象中请求值时,对象能够在错误发生前,有最后的机会响应这个请求。这样做有很多好处,下面的两个例子说明了这样做的好处。“
来至cocoa,这个说法应该挺有道理。
因为我们知道button却是存在一个highlighted实例变量.因此为何上面我们只是add一个相关的keypath就行了,
可以按照kvc查找的逻辑理解,就说的过去了。

6.What is purpose of delegates?
代理的作用?
答案:代理的目的是改变或传递控制链。允许一个类在某些特定时刻通知到其他类,而不需要获取到那些类的指针。可以减少框架复杂度。
另外一点,代理可以理解为java中的回调监听机制的一种类似。

7.What are mutable and immutable types in Objective C?
oc中可修改和不可以修改类型。
答案:可修改不可修改的集合类。这个我个人简单理解就是可动态添加修改和不可动态添加修改一样。
比如NSArray和NSMutableArray。前者在初始化后的内存控件就是固定不可变的,后者可以添加等,可以动态申请新的内存空间。

8.When we call objective c is runtime language what does it mean?
我们说的oc是动态运行时语言是什么意思?
答案:多态。 主要是将数据类型的确定由编译时,推迟到了运行时。
这个问题其实浅涉及到两个概念,运行时和多态。
简单来说,运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
多态:不同对象以自己的方式响应相同的消息的能力叫做多态。意思就是假设生物类(life)都用有一个相同的方法-eat;
那人类属于生物,猪也属于生物,都继承了life后,实现各自的eat,但是调用是我们只需调用各自的eat方法。
也就是不同的对象以自己的方式响应了相同的消息(响应了eat这个选择器)。
因此也可以说,运行时机制是多态的基础?~~~

9.what is difference between NSNotification and protocol?
通知和协议的不同之处?
答案:协议有控制链(has-a)的关系,通知没有。
首先我一开始也不太明白,什么叫控制链(专业术语了~)。但是简单分析下通知和代理的行为模式,我们大致可以有自己的理解
简单来说,通知的话,它可以一对多,一条消息可以发送给多个消息接受者。
代理按我们的理解,到不是直接说不能一对多,比如我们知道的明星经济代理人,很多时候一个经济人负责好几个明星的事务。
只是对于不同明星间,代理的事物对象都是不一样的,一一对应,不可能说明天要处理A明星要一个发布会,代理人发出处理发布会的消息后,别称B的
发布会了。但是通知就不一样,他只关心发出通知,而不关心多少接收到感兴趣要处理。
因此控制链(has-a从英语单词大致可以看出,单一拥有和可控制的对应关系。

10.What is push notification?
什么是推送消息?
答案: 
推送通知更是一种技术。
简单点就是客户端获取资源的一种手段。
普通情况下,都是客户端主动的pull。
推送则是服务器端主动push。 测试push的实现可以查看该博文。

11.Polymorphism?
关于多态性
答案:多态,父类指针指向子类对象。
这个题目其实可以出到一切面向对象语言中,
因此关于多态,继承和封装基本最好都有个自我意识的理解,也并非一定要把书上资料上写的能背出来。
最重要的是转化成自我理解。

12.Singleton?
对于单例的理解
答案:11,12题目其实出的有点泛泛的感觉了,可能说是编程语言需要或是必备的基础。
基本能用熟悉的语言写出一个单例,以及可以运用到的场景或是你编程中碰到过运用的此种模式的框架类等。
进一步点,考虑下如何在多线程访问单例时的安全性。

13.What is responder chain?
说说响应链
答案: 事件响应链。包括点击事件,画面刷新事件等。在视图栈内从上至下,或者从下之上传播。
可以说点事件的分发,传递以及处理。具体可以去看下touch事件这块。因为问的太抽象化了
严重怀疑题目出到越后面就越笼统。

可以从责任链模式,来讲通过事件响应链处理,其拥有的扩展性

14.Difference between frame and bounds?
frame和bounds有什么不同?
答案:frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父亲的坐标系统)
bounds指的是:该view在本身坐标系统中 的位置和大小。(参照点是本身坐标系统)

15.Difference between method and selector?
方法和选择器有何不同?
答案:selector是一个方法的名字,method是一个组合体,包含了名字和实现.
详情可以看apple文档。

16.Is there any garbage collection mechanism in Objective C.?
OC的垃圾回收机制?
答案: OC2.0有Garbage collection,但是iOS平台不提供。
一般我们了解的objective-c对于内存管理都是手动操作的,但是也有自动释放池。
但是差了大部分资料,貌似不要和arc机制搞混就好了。
求更多~~

17.NSOperation queue?
答案:存放NSOperation的集合类。
操作和操作队列,基本可以看成java中的线程和线程池的概念。用于处理ios多线程开发的问题。
网上部分资料提到一点是,虽然是queue,但是却并不是带有队列的概念,放入的操作并非是按照严格的先进现出。
这边又有个疑点是,对于队列来说,先进先出的概念是Afunc添加进队列,Bfunc紧跟着也进入队列,Afunc先执行这个是必然的,
但是Bfunc是等Afunc完全操作完以后,B才开始启动并且执行,因此队列的概念理论上有点违背了多线程处理这个概念。
但是转念一想其实可以参考银行的取票和叫号系统。
因此对于A比B先排队取票但是B率先执行完操作,我们亦然可以感性认为这还是一个队列。
但是后来看到一票关于这操作队列话题的文章,其中有一句提到
“因为两个操作提交的时间间隔很近,线程池中的线程,谁先启动是不定的。”
瞬间觉得这个queue名字有点忽悠人了,还不如pool~
综合一点,我们知道他可以比较大的用处在于可以帮助多线程编程就好了。

楼上区分了线程执行时的次序(Afunc和Bfunc谁先启动)和线程执行完成(Afunc和Bfunc谁先执行完)时的次序不同,而多线程的重要概念是并发(同时执行多个任务),NSOperationQueue是管理并发线程的对象,可以在其中放入NSOpertation对象(对象化的线程实体),通过设置maxConcurrentOperationCount的大小,控制并发数目,如楼上所说希望“Afunc添加进队列,执行完后,Bfunc紧跟进入队列,继续执行”,那只需将maxConcurrentOperationCount设为1,变会依次执行,这时候实际是在单线程依次执行。所以这里的NSOperationQueue就是对象化抽象的去管理多线程,这样的好处,使用者通过继承NSOperation对象,可以方便的用对象来管理线程,而不再用关心线程同步、信号量等细节,更多地关注于业务逻辑。

18.What is lazy loading?
答案:懒汉模式,只在用到的时候才去初始化。
也可以理解成延时加载。
我觉得最好也最简单的一个列子就是tableView中图片的加载显示了。
一个延时载,避免内存过高,一个异步加载,避免线程堵塞。

19.Can we use two tableview controllers on one viewcontroller?
是否在一个视图控制器中嵌入两个tableview控制器?
答案:一个视图控制只提供了一个View视图,理论上一个tableViewController也不能放吧,
只能说可以嵌入一个tableview视图。当然,题目本身也有歧义,如果不是我们定性思维认为的UIViewController,
而是宏观的表示视图控制者,那我们倒是可以把其看成一个视图控制者,它可以控制多个视图控制器,比如TabbarController
那样的感觉。

20.Can we use one tableview with two different datasources? How you will achieve this?
一个tableView是否可以关联两个不同的数据源?你会怎么处理?
答案:首先我们从代码来看,数据源如何关联上的,其实是在数据源关联的代理方法里实现的。
因此我们并不关心如何去关联他,他怎么关联上,方法只是让我返回根据自己的需要去设置如相关的数据源。
因此,我觉得可以设置多个数据源啊,但是有个问题是,你这是想干嘛呢?想让列表如何显示,不同的数据源分区块显示?

1.   Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?

Object-c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。

2.#import 跟#include 又什么区别,@class呢, #import<> 跟 #import”"又什么区别?

#import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入,相当于#include和#pragma once;@class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含;#import<>用来包含系统的头文件,#import””用来包含用户头文件。

3. 属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?

readwrite 是可读可写特性;需要生成getter方法和setter方法时

readonly 是只读特性  只会生成getter方法 不会生成setter方法 ;不希望属性在类外改变

assign 是赋值特性,setter方法将传入参数赋值给实例变量;仅设置变量时;

retain 表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1;

copy 表示拷贝特性,setter方法将传入对象复制一份;需要完全一份新的变量时。

nonatomic 非原子操作,决定编译器生成的setter getter是否是原子操作,atomic表示多线程安全,一般使用nonatomic

4.写一个setter方法用于完成@property (nonatomic,retain)NSString *name,写一个setter方法用于完成@property(nonatomic,copy)NSString *name

[cpp] view plaincopyprint?

  1. - (void)setName:(NSString *) str
  2. {
  3. [str retain];
  4. [name release];
  5. name = str;
  6. }
  7. - (void)setName:(NSString *)str
  8. {
  9. id t = [str copy];
  10. [name release];
  11. name = t;
  12. }

5.对于语句NSString*obj = [[NSData alloc] init]; obj在编译时和运行时分别时什么类型的对象?

编译时是NSString的类型;运行时是NSData类型的对象

6.常见的object-c的数据类型有那些, 和C的基本数据类型有什么区别?如:NSInteger和int

object-c的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,这些都是class,创建后便是对象,而C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值;NSInteger是基本数据类型,并不是NSNumber的子类,当然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是Long。

7.id 声明的对象有什么特性?

Id 声明的对象具有运行时的特性,即可以指向任意类型的objcetive-c的对象;

8.Objective-C如何对内存管理的,说说你的看法和解决方法?

Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。

9.内存管理的几条原则时什么?按照默认法则.那些关键字生成的对象

需要手动释放?在和property结合的时候怎样有效的避免内存泄露?

谁申请,谁释放

遵循Cocoa Touch的使用原则;

内存管理主要要避免“过早释放”和“内存泄漏”,对于“过早释放”需要注意@property设置特性时,一定要用对特性关键字,对于“内存泄漏”,一定要申请了要负责释放,要细心。

关键字alloc 或new 生成的对象需要手动释放;

设置正确的property属性,对于retain需要在合适的地方释放,

10.如何对iOS设备进行性能测试?

Profile-> Instruments ->Time Profiler

11.看下面的程序,第一个NSLog会输出什么?这时str的retainCount是多少?第二个和第三个呢? 为什么?

[cpp] view plaincopyprint?

  1. =======================================================
  2. NSMutableArray* ary = [[NSMutableArray array] retain];
  3. NSString *str = [NSString stringWithFormat:@"test"];
  4. [strretain];
  5. [aryaddObject:str];
  6. NSLog(@"%@%d",str,[str retainCount]);
  7. [strretain];
  8. [strrelease];
  9. [strrelease];
  10. NSLog(@"%@%d",str,[str retainCount]);
  11. [aryremoveAllObjects];
  12. NSLog(@"%@%d",str,[str retainCount]);
  13. =======================================================

str的retainCount创建+1,retain+1,加入数组自动+1

3

retain+1,release-1,release-1

2

数组删除所有对象,所有数组内的对象自动-1

1

12. Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?

线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject: afterDelay: 或者使用GCD的函数:dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});

13.描述一下iOS SDK中如何实现MVC的开发模式

MVC是模型、试图、控制开发模式,对于iOS SDK,所有的View都是视图层的,它应该独立于模型层,由视图控制层来控制。所有的用户数据都是模型层,它应该独立于视图。所有的ViewController都是控制层,由它负责控制视图,访问模型数据。

  1. 你使用过Objective-C的运行时编程(Runtime Programming)么?如果使用过,你用它做了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?  
    Objecitve-C的重要特性是Runtime(运行时),在#import <objc/runtime.h> 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用  
    ```objective-c
    Method method1 = class_getInstanceMethod(cls, sel1);
    Method method2 = class_getInstanceMethod(cls, sel2);
    method_exchangeImplementations(method1, method2);  
    ```   
    代码交换两个方法,在写unit test时使用到。  
    2. 你实现过多线程的Core Data么?NSPersistentStoreCoordinator,NSManagedObjectContext和NSManagedObject中的哪些需要在线程中创建或者传递?你是用什么样的策略来实现的?  
    没实现过多线程的CoreData(待实践) 
    <!--more--> 
    3. Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG做过些什么动画或者图像上的内容。(有需要的话还可以涉及Quartz的一些内容)  
    UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。    
    UIKit | 
    ------------ | 
    Core Animation | 
    Core Graphics |
    Graphics Hardware|  
    使用CA做过menu菜单的展开收起(太逊了)  
    4. 是否使用过CoreText或者CoreImage等?如果使用过,请谈谈你使用CoreText或者CoreImage的体验。
    CoreText可以解决复杂文字内容排版问题。CoreImage可以处理图片,为其添加各种效果。体验是很强大,挺复杂的。
    5. NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?(虽然protocol和delegate这种东西面试已经面烂了…)
    NSNotification是通知模式在iOS的实现,KVO的全称是键值观察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一个通过属性名访问属性变量的机制。例如将Module层的变化,通知到多个Controller对象时,可以使用NSNotification;如果是只需要观察某个对象的某个属性,可以使用KVO。
    对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向某个对象的,这是一对一的关系,而在通知模式中,往往是一对多的关系。委托模式,从技术上可以现在改变delegate指向的对象,但不建议这样做,会让人迷惑,如果一个delegate对象不断改变,指向不同的对象。  
    6. 你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。
    使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation都可以实现对线程的管理,区别是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
    项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
    更详细的答案见我的这篇文章
    7. 既然提到GCD,那么问一下在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?如何避免循环引用?
    使用block是要注意,若将block做函数参数时,需要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是能够读取其他函数内部变量的函数。更详细的答案见我的这篇文章
    8. 您是否做过异步的网络处理和通讯方面的工作?如果有,能具体介绍一些实现策略么?
    使用NSOperation发送异步网络请求,使用NSOperationQueue管理线程数目及优先级,底层是用NSURLConnetion,详细可见开源框架[LWConnetion](https://github.com/xunyn/LWConnetionDemo)。  
    9. 对于Objective-C,你认为它最大的优点和最大的不足是什么?对于不足之处,现在有没有可用的方法绕过这些不足来实现需求。如果可以的话,你有没有考虑或者实践过重新实现OC的一些功能,如果有,具体会如何做?
    最大的优点是它的运行时特性,不足是没有命名空间,对于命名冲突,可以使用长命名法或特殊前缀解决,如果是引入的第三方库之间的命名冲突,可以使用link命令及flag解决冲突。  
    10. 你实现过一个框架或者库以供别人使用么?如果有,请谈一谈构建框架或者库时候的经验;如果没有,请设想和设计框架的public的API,并指出大概需要如何做、需要注意一些什么方面,来使别人容易地使用你的框架。
    抽象和封装,方便使用。首先是对问题有充分的了解,比如构建一个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个解压请求,框架完成复杂文件的解压操作,并且在适当的时候通知给是哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系,通过抽象让其更为健壮、便于更改。其次是API的说明文档。

一.关于推送通知

来源:http://blog.csdn.net/enuola/article/details/8627283

推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能。是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的。一般说来,当要显示消息或 下载数据的时候,通知是由远程服务器(程序的提供者)发送,然后通过苹果的推送通知服务(Apple Push Notification Service,简称apns)推送到设备的程序上。

推送的新消息可能是一条信息、一项即将到期的日程或是一份远程服务器上的新数据。在系统上展现的时候,可以显示警告信息或在程序icon上显示数字,同 时,也可以播放警告音。一旦用户注意到程序有新的信息、时间或是数据,他们可以运行程序并访问新的内容。也可以选择忽略通知,这时程序将不会被激活。

iPhone, iPad和iPod touch上同一时刻只有一个app在前台运行。大多数程序在后台运行的时候,可以对某些用户感兴趣的内容做出回应(定时、或数据等)。推送通知能让程序在这些事件发生的时候通知用户。

作为提供者为程序开发和部署推送通知,必须通过iOS Developer Program Portal获得SSL证书。每个证书限用于一个程序,使用程序的bundle ID作为标识。证书有两种用途的:一种是针对sandbox(用于开发和测试),另外一种针对发布产品。这两种运行环境拥有为各自指定的IP地址并且需要 不同的证书。还必须为两种不同的环境获取各自的provisioning profiles。

APNS提供了两项基本的服务:消息推送和反馈服务。

消息推送:使用流式TCP套接字将推送通知作为二进制数据发送给APNs。消息推送有分别针对开发和测试用的sandbox、发布产品的两个接口,每个都 有各自的地址和端口。不管用哪个接口,都需要通过TLS或SSL,使用SSL证书来建立一个安全的信道。提供者编制通知信息,然后通过这个信道将其发送给 APNs。
注:sandbox:   gateway.sandbox.push.apple.com:219
产品接口:gateway.push.apple.com:2195

反馈服务:可以得到针对某个程序的发送失败记录。提供者应该使用反馈服务周期性检查哪些设备一直收不到通知,不需要重复发送通知到这些设备,降低推送服务器的负担。
注:sandbox:feedback.push.apple.com:2196
产品接口:feedback.sandbox.push.apple.com:2196

二.Apple Push Notification的工作机制

下面是一个完整推送流程图

从上图,我们可以看到。

  1. 首先是应用程序注册消息推送。
  2. IOS跟APNS Server要deviceToken。应用程序接受deviceToken。
  3. 应用程序将deviceToken发送给PUSH服务端程序(Provider)。
  4. 服务端程序向APNS服务发送消息。
  5. APNS服务将消息发送给iPhone应用程序。

无论是iPhone客户端跟APNS,还是Provider和APNS都需要通过证书进行连接的:

图中,

1. Provider是指某个iPhone软件的Push服务器,是我们将要开发的服务器。

2. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器。

上图可以分为三个阶段:

第一阶段:推送服务器(provider)把要发送的消息、目的iPhone的标识打包,发给APNS;

第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone;

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

  1. 使用开发者帐号登录IOS Provisioning ,选择或新建一个App Id,这里以“info.luoyl.iostest”为例
  2. 创建完后,进入App Id列表,可以看到新建的App Id默认是没有激活推送功能的,点击Configure链接,进入推送功能激活页面:
  3. 在“Enable for Apple Push Notification service”选项上打勾,然后在行点“configure”按钮:  此时会弹出一窗口,点“continue”
  4. 5. 弹出证书上传页面,证书选择事先做好的“CertificateSigningRequest.certSigningRequest”,然后点“Generate”按钮;
  5. 6. 接下来会有“Your APNs SSL Certificate has been generated.”提示,点“continue”:
  6. 7. 下载刚生成的证书“aps_development.cer”到电脑:
  7. 8. 至此,appid的Development Push SSL Certificate已经变成“Enabled”状态了:
  8. 9. 制作一开发者测试证书,appid指定为“info.luoyl.iostest”, 下载后双击安装到电脑上
  9. 10. 双击在步骤7下载的“aps_development.cer”安装到keychain Access上:
  10. 11. 选中push Services证书,右键导出证书为个人信息交换(.p12)格式文件,这里我命名为“aps_development.p12”,点存储时会弹出一个密码设置窗口,可留空不填:
  11. 12. 在终端执行下面的命令,把刚才导出的个人信息交换(.p12)格式文件加密转换成推送服务器的推送证书:

四.开发带有推送功能的IOS应用

为使应用能支持推送功能,我们的项目配置时要注意:

  1. Bundle Identifier、Code Signing指定的开发证书绑定的AppId要和推送证书绑定的AppId一致(见下图);
  2. 如 果项目中的开发证书在AppId激活推送功能前已经创建了,这时必须重新生成一个。支持推送功能的开发证书会比旧证书多出一项名为 “aps- environment”的授权串,如果继续使用旧证书,在程序启动尝试注册推送功能时会出现“ 未找到应用程序的“aps-environment”的权利字符串 ”的错误;
  3. 测试需要用真机,模拟器不支持。

在代码方面,推送的注册、监听和处理都集中在AppDelegate类里:

1.(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
在该方法体里主要实现两个功能:
一是完成推送功能的注册请求,即在程序启动时弹出是否使用推送功能;
二是实现的程序启动是通过推送消息窗口触发的,在这里可以处理推送内容;

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  2. {
  3. self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
  4. // Override point for customization after application launch.
  5. self.viewController = [[[ViewController alloc] init] autorelease];
  6. self.window.rootViewController = self.viewController;
  7. [self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"background.png"]]];
  8. [self.window makeKeyAndVisible];
  9. /** 注册推送通知功能, */
  10. [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
  11. //判断程序是不是由推送服务完成的
  12. if (launchOptions) {
  13. NSDictionary* pushNotificationKey = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
  14. if (pushNotificationKey) {
  15. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"推送通知"
  16. message:@"这是通过推送窗口启动的程序,你可以在这里处理推送内容"
  17. delegate:nil
  18. cancelButtonTitle:@"知道了"
  19. otherButtonTitles:nil, nil];
  20. [alert show];
  21. [alert release];
  22. }
  23. }
  24. return YES;
  25. }

2. 接收从苹果服务器返回的唯一的设备token,该token是推送服务器发送推送消息的依据,所以需要发送回推送服务器保存

  1. - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  2. NSString* token = [NSString stringWithFormat:@"%@",deviceToken];
  3. NSLog(@"apns -> 生成的devToken:%@", token);
  4. //把deviceToken发送到我们的推送服务器
  5. DeviceSender* sender = [[[DeviceSender alloc]initWithDelegate:self ]autorelease];
  6. [sender sendDeviceToPushServer:token ];
  7. }

3.接收注册推送通知功能时出现的错误,并做相关处理:

  1. - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
  2. NSLog(@"apns -> 注册推送功能时发生错误, 错误信息:\n %@", err);
  3. }

4. 接收到推送消息,解析处理

  1. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
  2. {
  3. NSLog(@"\napns -> didReceiveRemoteNotification,Receive Data:\n%@", userInfo);
  4. //把icon上的标记数字设置为0,
  5. application.applicationIconBadgeNumber = 0;
  6. if ([[userInfo objectForKey:@"aps"] objectForKey:@"alert"]!=NULL) {
  7. UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"**推送消息**"
  8. message:[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]
  9. delegate:self
  10. cancelButtonTitle:@"关闭"
  11. otherButtonTitles:@"处理推送内容",nil];
  12. alert.tag = alert_tag_push;
  13. [alert show];
  14. }
  15. }

通过上面的代码,基本推送功能的开发已经完成了。最后附件上面代码中所需用到的DeviceSender的类文件,需要将其头文件导入到AppDelegate中。

注意:有的App ID的Apple Push Notification service列是灰色的,并且不允许使用Configure按钮,这是因为APNS不支持带通配符的App ID。

到现在为止,我们已经生成了三个文件:

1、Push.certSigningRequest

2、Push.p12

3、aps_developer_identity.cer

在项目的AppDelegate中的didFinishLaunchingWithOptions方法中加入下面的代码:

  1. [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge)];

通过registerForRemoteNotificationTypes方法,告诉应用程序,能接受push来的通知。

在项目的AppDelegate中添加下面的方法来获取deviceToken:

  1. - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  2. NSString *token = [NSString stringWithFormat:@"%@", deviceToken];
  3. NSLog(@"My token is:%@", token);
  4. }
  5. - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  6. NSString *error_str = [NSString stringWithFormat: @"%@", error];
  7. NSLog(@"Failed to get token, error:%@", error_str);
  8. }

获取到的deviceToken,我们可以提交给后台应用程序,发送通知的后台应用程序除了需要知道deviceToken之外,还需要一个与APNS连接的证书。

这个证书可以通过我们前面生成的两个文件中得到。

1、将aps_developer_identity.cer转换成aps_developer_identity.pem格式

  1. openssl x509 -in aps_developer_identity.cer -inform DER -out aps_developer_identity.pem -outform PEM

2、将p12格式的私钥转换成pem

  1. openssl pkcs12 -nocerts -out Push_Noenc.pem -in Push.p12

3、创建p12文件

  1. openssl pkcs12 -export -in aps_developer_identity.pem -inkey Push_Noenc.pem -certfile Push.certSigningRequest -name "aps_developer_identity" -out aps_developer_identity.p12

这样我们就得到了在.net或java等后台应用程序中使用的证书文件:aps_developer_identity.p12

如果后台应用是php的话,那么可以按照iOS消息推送机制中pem文件的生成这篇文章中的方法来生成php后台应用程序中使用的证书文件:ck.pem

SQLite是嵌入式的和轻量级的sql数据库。广泛用于包括浏览器、ios、android以及一些便携需求的小型web应用系统。

SQLite是MySQL的精简版,无需服务器就能进行;限制条件:必须手动创建数据库,没有面向对象的接口;

Demo做了个简单的保存学生信息的例子,点击保存按钮可以保存信息,点击查询可以查询信息,Demo下载地址:http://download.csdn.net/detail/weasleyqi/4706760 。

要想在工程中使用SQLite,需要将SQLite的库添加到工程:

在本工程中的.h文件中引用这个库:

[css] view plaincopy

  1. <span style="font-size:14px;">#import "sqlite3.h"</span>

创建数据库:

接下来如果该数据库不存在需要创建这个数据库,创建的过程写在viewDidLoad里面:

[css] view plaincopy

  1. <span style="font-size:14px;">- (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. // Do any additional setup after loading the view, typically from a nib.
  5. NSString *docsDir;
  6. NSArray *dirPaths;
  7. dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  8. docsDir = [dirPaths objectAtIndex:0];
  9. databasePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:@"info.db"]];
  10. NSFileManager *filemanager = [NSFileManager defaultManager];
  11. if ([filemanager fileExistsAtPath:databasePath] == NO) {
  12. const char *dbpath = [databasePath UTF8String];
  13. if (sqlite3_open(dbpath, &dataBase)==SQLITE_OK) {
  14. char *errmsg;
  15. const char *createsql = "CREATE TABLE IF NOT EXISTS INFO (ID INTEGER PRIMARY KEY AUTOINCREMENT, NUM TEXT, CLASSNAME TEXT,NAME TEXT)";
  16. if (sqlite3_exec(dataBase, createsql, NULL, NULL, &errmsg)!=SQLITE_OK) {
  17. status.text = @"create table failed.";
  18. }
  19. }
  20. else {
  21. status.text = @"create/open failed.";
  22. }
  23. }
  24. }</span>

因为SQLite数据库是文件数据库,是保存在文件系统中的,ios下:

  • Documents:应用中用户数据可以放在这里,iTunes备份和恢复的时候会包括此目录
  • tmp:存放临时文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除
  • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除

我们的数据库文件是保存在Documents下的。

切记,因为用的是C语法,sqlite3_open传入的是database的地址!

保存信息:

[css] view plaincopy

  1. <span style="font-size:14px;">- (IBAction)saveinfo:(id)sender {
  2. sqlite3_stmt *statement;
  3. const char *dbpath = [databasePath UTF8String];
  4. if (sqlite3_open(dbpath, &dataBase)==SQLITE_OK) {
  5. if ([num.text isEqualToString:@""]) {
  6. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SORRY!" message:@"number cannot be nil!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
  7. [alert show];
  8. }
  9. else {
  10. NSString *insertSql = [NSString stringWithFormat:@"INSERT INTO INFO (num,classname,name) VALUES(\"%@\",\"%@\",\"%@\")",num.text,classname.text,name.text];
  11. const char *insertstaement = [insertSql UTF8String];
  12. sqlite3_prepare_v2(dataBase, insertstaement, -1, &statement, NULL);
  13. if (sqlite3_step(statement)==SQLITE_DONE) {
  14. status.text = @"save to DB.";
  15. num.text = @"";
  16. classname.text = @"";
  17. name.text = @"";
  18. }
  19. else {
  20. status.text = @"save failed!";
  21. }
  22. sqlite3_finalize(statement);
  23. sqlite3_close(dataBase);
  24. }
  25. }
  26. }</span>

在往数据库里面插入数据的时候,我们需要先打开数据库,然后执行插入语句,结束的时候切记要关闭数据库!

查询操作:

[css] view plaincopy

  1. <span style="font-size:14px;">- (IBAction)searchResult:(id)sender {
  2. const char *dbpath = [databasePath UTF8String];
  3. sqlite3_stmt *statement;
  4. if (sqlite3_open(dbpath, &dataBase)==SQLITE_OK) {
  5. NSString *querySQL = [NSString stringWithFormat:@"SELECT classname,name from info where num=\"%@\"",num.text];
  6. const char *querystatement = [querySQL UTF8String];
  7. if (sqlite3_prepare_v2(dataBase, querystatement, -1, &statement, NULL)==SQLITE_OK) {
  8. if (sqlite3_step(statement)==SQLITE_ROW) {
  9. NSString *classnameField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)];
  10. classname.text = classnameField;
  11. NSString *nameField = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
  12. name.text = nameField;
  13. status.text = @"find~~~";
  14. }
  15. else {
  16. status.text = @"did not find you need.";
  17. }
  18. sqlite3_finalize(statement);
  19. }
  20. sqlite3_close(dataBase);
  21. }
  22. }</span>

查询操作同样也是需要先打开数据库,再查询,最后关闭数据库,在这里就指定了根据学号来查询,其他情况未涉及。

在本例中还涉及一个触摸屏幕来关闭键盘:

在viewcontroller.h中添加申明代码:

[css] view plaincopy

  1. <span style="font-size:14px;">- (IBAction)backgroundTap:(id)sender;
  2. </span>

通过触摸屏幕来关闭键盘需要我们的.xib文件的class为UIControl,点击viewController.xib文件,选中view,打开 Identity Inspector,在class中选择UIConrol,再选择Connector Inspector,找到Touch Down,把圆圈中的线映射到刚刚的IBAction;

在viewController.m文件中实现该方法:

[css] view plaincopy

  1. - (IBAction)backgroundTap:(id)sender {
  2. [num resignFirstResponder];
  3. [classname resignFirstResponder];
  4. [name resignFirstResponder];
  5. }

这样,基本上就完成了

一、离线缓存

在项目开发中,通常都需要对数据进行离线缓存的处理,如新闻数据的离线缓存等。

说明:离线缓存一般都是把数据保存到项目的沙盒中。有以下几种方式

(1)归档:NSCodeing、NSKeyedArchiver

(2)偏好设置:NSUserDefaults

(3)Plist存储:writeToFile

提示:上述三种方法都有一个致命的缺点,那就是都无法存储大批量的数据,有性能的问题。

举例:使用归档

两个问题:

(1)数据的存取都必须是完整的,要求写入的时候要一次性写入,读取的时候要一次性全部读取,这涉及到应用的性能问题。

(2)如果有1000条数据,此时要把第1001条数据存入,那么需要把所有的数据取出来,把这条数据加上去之后,再存入。

说明:以上的三种技术不能处理大批量数据的存储,大批量数据通常使用数据库来进行存储。

二、SQLite简单介绍

1.ios中数据的存储方式

(1)Plist(NSArray\NSDictionary)

(2)Preference(偏好设置\NSUserDefaults)

(3)NSCoding(NSKeyedArchiver\NSkeyedUnarchiver)

(4)SQLite3

  (5)Core Data

说明:

3是版本号,是SQLite的第三个版本。

core Data是对SQLite的封装,因为iOS中使用的SQLite是纯C语言的。

2.SQLite

(1)什么是SQLite?

答:SQLite是一款轻型的嵌入式数据库,安卓和ios开发使用的都是SQLite数据库

(2)特点(优点)

  答:1)它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了

  2)它的处理速度比Mysql、PostgreSQL这两款著名的数据库都还快

(3)什么是数据库

答:数据库(Database)是按照数据结构来组织、存储和管理数据的仓库

(4)数据库的分类

答:可以分为2大种类

关系型数据库(主流)和对象型数据库(直接把内存中的对象塞入到数据库,对比关系型数据库而言性能不能很好,效率不高)

(5)常用关系型数据库有哪些?

答:PC端:Oracle、MySQL、SQL Server、Access、DB2、Sybase

  嵌入式\移动客户端:SQLite

(6)数据库是如何存储数据的?

答:数据库的存储结构和excel很像,以表(table)为单位 。表由多个字段(列、属性、column)组成,表里面的每一行数据称为记录

(7)数据库存储数据的步骤?

1)新建一张表(table)

2)添加多个字段(column,列,属性)

3)添加多行记录(row,record,每行存放多个字段对应的值)

1 定位服务

iOS设备提供三种不同定位途径,蜂窝式移动电话基站定位;WiFi定位,通过查询一个WiFi路由器的地理位置信息,比较省电;GPS卫星定位,通过3~4颗卫星定位,最为准确,但是耗电量大。iOS系统如果能够接收GPS信息,那么设备优先采用GPS,其次是WiFi,最后是基站,开发人员不能选择哪种定位方式。

定位服务使用CoreLocation框架,主要使用CLLocationMananger、CLLocationManangerDelegate和CLLocation三个类,CLLocationMananger是定位服务管理类,获取设备的位置信息,CLLocationManangerDelegate是代理协议,CLLocation封装了位置信息。

这里要注意,CLLocationManangerDelegate 的locationManager:didUpdateToLocation:fromLocation:方法得到的坐标是火星坐标,这个原因你懂得,所以需要转换成真实的地理坐标。我使用的是一个第三方的CSqlite类,有一个转换坐标的数据库,你调用就可以转换为正确坐标了。

得到经纬度后,要进行地理位置信息反编码,使用CLGeocoder类实现,将地理坐标转换为地理文字描述信息,这些文字描述信息被封装在CLPlacemark类中。

当然给定地理信息的文字描述,也可以进行地理信息编码查询,转换为地理坐标,也是采用CLGeocoder类。

判断一个坐标点是否在一个无规则的多边形内

?

//    在范围内返回1,不在返回0

-(int)mutableBoundConrtolAction:(NSMutableArray *)arrSome:(CLLocationCoordinate2D )myCoordinate4{

int n=arrSome.count;

float vertx[n];

float verty[n];

for (int i=0; i<arrSome.count; i++) {

//MyPoint类存储的是经度和纬度

vertx[i]=((MyPoint *)(arrSome[i])).x;

verty[i]=((MyPoint *)(arrSome[i])).y;

}

if (arrSome.count==0) {

return 1;

}

BOOL i=pnpoly(arrSome.count, vertx, verty, myCoordinate4.latitude, myCoordinate4.longitude);

if (i) {

return 1;

}else{

return 0;

}

return 1;

}

//多边形由边界的坐标点所构成的数组组成,参数格式 该数组的count,  多边形边界点x坐标 的组成的数组,多边形边界点y坐标 的组成的数组,需要判断的点的x坐标,需要判断的点的y坐标

BOOL pnpoly (int nvert, float *vertx, float *verty, float testx, float testy) {

int i, j;

BOOL c=NO;

for (i = 0, j = nvert-1; i < nvert; j = i++) {

if ( ( (verty[i]>testy) != (verty[j]>testy) ) &&

(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )

c = !c;

}

return c;

}

2 系统地图

地图我目前用过系统、百度以及高德,开发人员使用都是差不多的,下面的代码涉及的类都是高德地图api提供的类。

我之前做项目,使用高德地图,做到后期,项目会出现闪退,后来查出是地图区域内存的问题,然后重新布局了地图区域,使得每一个地图区域能够及时销毁,虽然闪退周期明显延长,但是还是存在,这里不知道是何原因,说来惭愧。

设置地图区域

?

1

2

3

4

5

6

7

8

9

-(void)SetMapRegion:(CLLocationCoordinate2D)myCoordinate

{

MACoordinateRegion theRegion = { {0.0, 0.0 }, { 0.0, 0.0 } };

theRegion.center=myCoordinate;

[self.m_map setScrollEnabled:YES];

theRegion.span.longitudeDelta = 0.01f;

theRegion.span.latitudeDelta = 0.01f;

[self.m_map setRegion:theRegion animated:YES];

}

平移地图,上下左右

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

-(void)panMap:(NSString *)direction{

CLLocationCoordinate2D changeCoordinate=self.m_map.centerCoordinate;

CGPoint changePoint=[self.m_map convertCoordinate:changeCoordinate toPointToView:self.m_map];

if ([direction isEqualToString:@"up"]) {

changePoint.y=changePoint.y+50;

}else if ([direction isEqualToString:@"down"]) {

changePoint.y=changePoint.y-50;

}else if ([direction isEqualToString:@"left"]) {

changePoint.x=changePoint.x-50;

}else if ([direction isEqualToString:@"right"]) {

changePoint.x=changePoint.x+50;

}

changeCoordinate=[self.m_map convertPoint:changePoint toCoordinateFromView:self.m_map];

[self.m_map setCenterCoordinate:changeCoordinate animated:YES];

}

判断某一个坐标点是否在当前地图区域内

?

1

2

3

4

5

6

7

8

9

10

-(void)isAtCurrentRegion:(CLLocationCoordinate2D)coordiante{

CGPoint point=[self.m_map convertCoordinate:coordiante toPointToView:self.view];

if ((point.x<0)||(point.y<0)||(point.x>WScreen)||(point.y>HScreen)) {

//        如果不在 设置该点为地图中心点

[self SetMapRegion:coordiante];

}

}

在地图上添加标注

系统地图使用MapKit框架,核心是MKMapView类,显示地图只要添加MKMapView实例就可以了。如果要实现在地图上添加标注点,第以是触发添加动作,第二实现MKMapViewDelegate的mapView:viewForAnnotation:完成添加标注。

高德地图实现的原理也是一样的,高德地图使用的是MAMapKit框架。对于annotation,一般会自定义一个继承NSobject并且实现了maannotation协议的类,然后使用mapview的addAnnotation:方法就可以。MKReverseGeocoder类可以实现coordinate的反编码,这里需要实现它的代理,把得到的地理文字描述信息赋给annotation。这里需要实现代理的mapView:viewForAnnotation:方法,一个标注其实就是一个MAAnnotationView,标注有点类似tableviewcell,这里也有重用机制。实现代理的mapView:annotationView:calloutAccessoryControlTapped:方法可以响应leftCalloutAccessoryView或者rightCalloutAccessoryView的点击事件,不过这个accessory view必须继承自UIControl。

在地图上绘制线条和多边形

MAPolyline类定义一个由多个点相连的多段线,点与点之间尾部想连但第一点与最后一个点不相连, 通常MAPolyline是MAPolylineView的model,它提供了两个方法polylineWithPoints:count:、polylineWithCoordinates:count:用来添加线条,然后再通过map view的addOverlay:方法把Polyline实例添加进去,最后实现mapviewdelegate的mapView:viewForOverlay:方法就可以了。注意如果一开始添加的不是coordinate,而是point,可以通过map view的convertPoint:toCoordinateFromView:方法进行转换。

MAPolygon类定义的就是一个不规则的由多个点组成的闭合多边形,点与点之间按顺序尾部相连, 第一个点与最后一个点相连, 通常MAPolygon是MAPolygonView的model,首先需要添加坐标点的数组,可以使用polygonWithCoordinates:count:方法或者polygonWithPoints:count:方法,然后把这个polygon通过addOverlay:方法添加到map view上就可以了。然后可以在mapviewdelegate里面的mapView:viewForOverlay:方法里面给MAPolygonView的属性赋值,这样一个完整的多边形就出来了。

不管是高德地图还是百度地图等第三方,都会有一个mapsearchkit,这是一个用于查询的框架,有兴趣的朋友可以多加研究。

iOS中的MapKit集成了google地图api的很多功能加上iOS的定位的功能,我们就可以实现将你运行的轨迹绘制到地图上面。这个功能非常有用,比如快递追踪、汽车的gprs追踪、人员追踪等等。这篇文章我们将使用Map Kit和iOS的定位功能,将你的运行轨迹绘制在地图上面。

实现

在之前的一篇文章描述了如何在地图上显示自己的位置,如果我们将这些位置先保存起来,然后串联起来绘制到地图上面,那就是我们的运行轨迹了。

首先我们看下如何在地图上绘制曲线。在Map Kit中提供了一个叫MKPolyline的类,我们可以利用它来绘制曲线,先看个简单的例子。

使用下面代码从一个文件中读取出经纬度,然后创建一个路径:MKPolyline实例。

[plain] view plaincopy

  1. -(void) loadRoute
  2. {
  3. NSString* filePath = [[NSBundle mainBundle] pathForResource:@”route” ofType:@”csv”];
  4. NSString* fileContents = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
  5. NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  6. // while we create the route points, we will also be calculating the bounding box of our route
  7. // so we can easily zoom in on it.
  8. MKMapPoint northEastPoint;
  9. MKMapPoint southWestPoint;
  10. // create a c array of points.
  11. MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * pointStrings.count);
  12. for(int idx = 0; idx < pointStrings.count; idx++)
  13. {
  14. // break the string down even further to latitude and longitude fields.
  15. NSString* currentPointString = [pointStrings objectAtIndex:idx];
  16. NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
  17. CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
  18. CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
  19. // create our coordinate and add it to the correct spot in the array
  20. CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
  21. MKMapPoint point = MKMapPointForCoordinate(coordinate);
  22. //
  23. // adjust the bounding box
  24. //
  25. // if it is the first point, just use them, since we have nothing to compare to yet.
  26. if (idx == 0) {
  27. northEastPoint = point;
  28. southWestPoint = point;
  29. }
  30. else
  31. {
  32. if (point.x > northEastPoint.x)
  33. northEastPoint.x = point.x;
  34. if(point.y > northEastPoint.y)
  35. northEastPoint.y = point.y;
  36. if (point.x < southWestPoint.x)
  37. southWestPoint.x = point.x;
  38. if (point.y < southWestPoint.y)
  39. southWestPoint.y = point.y;
  40. }
  41. pointArr[idx] = point;
  42. }
  43. // create the polyline based on the array of points.
  44. self.routeLine = [MKPolyline polylineWithPoints:pointArr count:pointStrings.count];
  45. _routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
  46. // clear the memory allocated earlier for the points
  47. free(pointArr);
  48. }

[plain] view plaincopy

  1. -(void) loadRoute
  2. {
  3. NSString* filePath = [[NSBundle mainBundle] pathForResource:@”route” ofType:@”csv”];
  4. NSString* fileContents = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
  5. NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  6. // while we create the route points, we will also be calculating the bounding box of our route
  7. // so we can easily zoom in on it.
  8. MKMapPoint northEastPoint;
  9. MKMapPoint southWestPoint;
  10. // create a c array of points.
  11. MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * pointStrings.count);
  12. for(int idx = 0; idx < pointStrings.count; idx++)
  13. {
  14. // break the string down even further to latitude and longitude fields.
  15. NSString* currentPointString = [pointStrings objectAtIndex:idx];
  16. NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
  17. CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
  18. CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
  19. // create our coordinate and add it to the correct spot in the array
  20. CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
  21. MKMapPoint point = MKMapPointForCoordinate(coordinate);
  22. //
  23. // adjust the bounding box
  24. //
  25. // if it is the first point, just use them, since we have nothing to compare to yet.
  26. if (idx == 0) {
  27. northEastPoint = point;
  28. southWestPoint = point;
  29. }
  30. else
  31. {
  32. if (point.x > northEastPoint.x)
  33. northEastPoint.x = point.x;
  34. if(point.y > northEastPoint.y)
  35. northEastPoint.y = point.y;
  36. if (point.x < southWestPoint.x)
  37. southWestPoint.x = point.x;
  38. if (point.y < southWestPoint.y)
  39. southWestPoint.y = point.y;
  40. }
  41. pointArr[idx] = point;
  42. }
  43. // create the polyline based on the array of points.
  44. self.routeLine = [MKPolyline polylineWithPoints:pointArr count:pointStrings.count];
  45. _routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
  46. // clear the memory allocated earlier for the points
  47. free(pointArr);
  48. }

将这个路径添加到地图上

[plain] view plaincopy

  1. [self.mapView addOverlay:self.routeLine];

[plain] view plaincopy

  1. [self.mapView addOverlay:self.routeLine];

显示在地图上:

[plain] view plaincopy

  1. - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay
  2. {
  3. MKOverlayView* overlayView = nil;
  4. if(overlay == self.routeLine)
  5. {
  6. //if we have not yet created an overlay view for this overlay, create it now.
  7. if(nil == self.routeLineView)
  8. {
  9. self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
  10. self.routeLineView.fillColor = [UIColor redColor];
  11. self.routeLineView.strokeColor = [UIColor redColor];
  12. self.routeLineView.lineWidth = 3;
  13. }
  14. overlayView = self.routeLineView;
  15. }
  16. return overlayView;
  17. }

[plain] view plaincopy

  1. - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id )overlay
  2. {
  3. MKOverlayView* overlayView = nil;
  4. if(overlay == self.routeLine)
  5. {
  6. //if we have not yet created an overlay view for this overlay, create it now.
  7. if(nil == self.routeLineView)
  8. {
  9. self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
  10. self.routeLineView.fillColor = [UIColor redColor];
  11. self.routeLineView.strokeColor = [UIColor redColor];
  12. self.routeLineView.lineWidth = 3;
  13. }
  14. overlayView = self.routeLineView;
  15. }
  16. return overlayView;
  17. }

效果:

然后我们在从文件中读取位置的方法改成从用gprs等方法获取当前位置。

第一步:创建一个CLLocationManager实例

第二步:设置CLLocationManager实例委托和精度

第三步:设置距离筛选器distanceFilter

第四步:启动请求

代码如下:

[plain] view plaincopy

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. noUpdates = 0;
  4. locations = [[NSMutableArray alloc] init];
  5. locationMgr = [[CLLocationManager alloc] init];
  6. locationMgr.delegate = self;
  7. locationMgr.desiredAccuracy =kCLLocationAccuracyBest;
  8. locationMgr.distanceFilter  = 1.0f;
  9. [locationMgr startUpdatingLocation];
  10. }

[plain] view plaincopy

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. noUpdates = 0;
  4. locations = [[NSMutableArray alloc] init];
  5. locationMgr = [[CLLocationManager alloc] init];
  6. locationMgr.delegate = self;
  7. locationMgr.desiredAccuracy =kCLLocationAccuracyBest;
  8. locationMgr.distanceFilter  = 1.0f;
  9. [locationMgr startUpdatingLocation];
  10. }

上面的代码我定义了一个数组,用于保存运行轨迹的经纬度。

每次通知更新当前位置的时候,我们将当前位置的经纬度放到这个数组中,并重新绘制路径,代码如下:

[plain] view plaincopy

  1. - (void)locationManager:(CLLocationManager *)manager
  2. didUpdateToLocation:(CLLocation *)newLocation
  3. fromLocation:(CLLocation *)oldLocation{
  4. noUpdates++;
  5. [locations addObject: [NSString stringWithFormat:@"%f,%f",[newLocation coordinate].latitude, [newLocation coordinate].longitude]];
  6. [self updateLocation];
  7. if (self.routeLine!=nil) {
  8. self.routeLine =nil;
  9. }
  10. if(self.routeLine!=nil)
  11. [self.mapView removeOverlay:self.routeLine];
  12. self.routeLine =nil;
  13. // create the overlay
  14. [self loadRoute];
  15. // add the overlay to the map
  16. if (nil != self.routeLine) {
  17. [self.mapView addOverlay:self.routeLine];
  18. }
  19. // zoom in on the route.
  20. [self zoomInOnRoute];
  21. }

[plain] view plaincopy

  1. - (void)locationManager:(CLLocationManager *)manager
  2. didUpdateToLocation:(CLLocation *)newLocation
  3. fromLocation:(CLLocation *)oldLocation{
  4. noUpdates++;
  5. [locations addObject: [NSString stringWithFormat:@"%f,%f",[newLocation coordinate].latitude, [newLocation coordinate].longitude]];
  6. [self updateLocation];
  7. if (self.routeLine!=nil) {
  8. self.routeLine =nil;
  9. }
  10. if(self.routeLine!=nil)
  11. [self.mapView removeOverlay:self.routeLine];
  12. self.routeLine =nil;
  13. // create the overlay
  14. [self loadRoute];
  15. // add the overlay to the map
  16. if (nil != self.routeLine) {
  17. [self.mapView addOverlay:self.routeLine];
  18. }
  19. // zoom in on the route.
  20. [self zoomInOnRoute];
  21. }

我们将前面从文件获取经纬度创建轨迹的代码修改成从这个数组中取值就行了:

[plain] view plaincopy

  1. // creates the route (MKPolyline) overlay
  2. -(void) loadRoute
  3. {
  4. // while we create the route points, we will also be calculating the bounding box of our route
  5. // so we can easily zoom in on it.
  6. MKMapPoint northEastPoint;
  7. MKMapPoint southWestPoint;
  8. // create a c array of points.
  9. MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * locations.count);
  10. for(int idx = 0; idx < locations.count; idx++)
  11. {
  12. // break the string down even further to latitude and longitude fields.
  13. NSString* currentPointString = [locations objectAtIndex:idx];
  14. NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
  15. CLLocationDegrees latitude  = [[latLonArr objectAtIndex:0] doubleValue];
  16. CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
  17. CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
  18. MKMapPoint point = MKMapPointForCoordinate(coordinate);
  19. if (idx == 0) {
  20. northEastPoint = point;
  21. southWestPoint = point;
  22. }
  23. else
  24. {
  25. if (point.x > northEastPoint.x)
  26. northEastPoint.x = point.x;
  27. if(point.y > northEastPoint.y)
  28. northEastPoint.y = point.y;
  29. if (point.x < southWestPoint.x)
  30. southWestPoint.x = point.x;
  31. if (point.y < southWestPoint.y)
  32. southWestPoint.y = point.y;
  33. }
  34. pointArr[idx] = point;
  35. }
  36. self.routeLine = [MKPolyline polylineWithPoints:pointArr count:locations.count];
  37. _routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
  38. free(pointArr);
  39. }

[plain] view plaincopy

  1. // creates the route (MKPolyline) overlay
  2. -(void) loadRoute
  3. {
  4. // while we create the route points, we will also be calculating the bounding box of our route
  5. // so we can easily zoom in on it.
  6. MKMapPoint northEastPoint;
  7. MKMapPoint southWestPoint;
  8. // create a c array of points.
  9. MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * locations.count);
  10. for(int idx = 0; idx < locations.count; idx++)
  11. {
  12. // break the string down even further to latitude and longitude fields.
  13. NSString* currentPointString = [locations objectAtIndex:idx];
  14. NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
  15. CLLocationDegrees latitude  = [[latLonArr objectAtIndex:0] doubleValue];
  16. CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
  17. CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(latitude, longitude);
  18. MKMapPoint point = MKMapPointForCoordinate(coordinate);
  19. if (idx == 0) {
  20. northEastPoint = point;
  21. southWestPoint = point;
  22. }
  23. else
  24. {
  25. if (point.x > northEastPoint.x)
  26. northEastPoint.x = point.x;
  27. if(point.y > northEastPoint.y)
  28. northEastPoint.y = point.y;
  29. if (point.x < southWestPoint.x)
  30. southWestPoint.x = point.x;
  31. if (point.y < southWestPoint.y)
  32. southWestPoint.y = point.y;
  33. }
  34. pointArr[idx] = point;
  35. }
  36. self.routeLine = [MKPolyline polylineWithPoints:pointArr count:locations.count];
  37. _routeRect = MKMapRectMake(southWestPoint.x, southWestPoint.y, northEastPoint.x - southWestPoint.x, northEastPoint.y - southWestPoint.y);
  38. free(pointArr);
  39. }

这样我们就将我们运行得轨迹绘制google地图上面了。

扩展:

如果你想使用其他的地图,比如百度地图,其实也很方便。可以将百度地图放置到UIWebView中间,通过用js去绘制轨迹。