前言

今天给大家详细介绍一下iOS之短信,邮件,通信录技术的具体实现

1. 系统应用之短信,邮件

在开发某些应用时可能希望能够调用iOS系统内置的电话、短信、邮件、浏览器应用,此时你可以直接使用UIApplication的OpenURL:方法指定特定的协议来打开不同的系统应用。常用的协议如下:
打电话:tel:或者tel://、telprompt:或telprompt://(拨打电话前有提示)
发短信:sms:或者sms://
发送邮件:mailto:或者mailto://
启动浏览器:http:或者http://
下面以一个简单的demo演示如何调用上面几种系统应用:

#import "ViewController.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];}#pragma mark - UI事件
//打电话
- (IBAction)callClicK:(UIButton *)sender {NSString *phoneNumber=@"18500138888";
//    NSString *url=[NSString stringWithFormat:@"tel://%@",phoneNumber];//这种方式会直接拨打电话NSString *url=[NSString stringWithFormat:@"telprompt://%@",phoneNumber];//这种方式会提示用户确认是否拨打电话[self openUrl:url];
}//发送短信
- (IBAction)sendMessageClick:(UIButton *)sender {NSString *phoneNumber=@"18500138888";NSString *url=[NSString stringWithFormat:@"sms://%@",phoneNumber];[self openUrl:url];
}//发送邮件
- (IBAction)sendEmailClick:(UIButton *)sender {NSString *mailAddress=@"kenshin@hotmail.com";NSString *url=[NSString stringWithFormat:@"mailto://%@",mailAddress];[self openUrl:url];
}//浏览网页
- (IBAction)browserClick:(UIButton *)sender {NSString *url=@"http://www.cnblogs.com/kenshincui";[self openUrl:url];
}#pragma mark - 私有方法
-(void)openUrl:(NSString *)urlStr{//注意url中包含协议名称,iOS根据协议确定调用哪个应用,例如发送邮件是“sms://”其中“//”可以省略写成“sms:”(其他协议也是如此)NSURL *url=[NSURL URLWithString:urlStr];UIApplication *application=[UIApplication sharedApplication];if(![application canOpenURL:url]){NSLog(@"无法打开\"%@\",请确保此应用已经正确安装.",url);return;}[[UIApplication sharedApplication] openURL:url];
}@end

不难发现当openURL:方法只要指定一个URL Schame并且已经安装了对应的应用程序就可以打开此应用。当然,如果是自己开发的应用也可以调用openURL方法来打开。假设你现在开发了一个应用A,如果用户机器上已经安装了此应用,并且在应用B中希望能够直接打开A。那么首先需要确保应用A已经配置了Url Types,具体方法就是在plist文件中添加URL types节点并配置URL Schemas作为具体协议,配置URL identifier作为这个URL的唯一标识,如下图
这里写图片描述
然后就可以调用openURL方法像打开系统应用一样打开第三方应用程序了:

//打开第三方应用
- (IBAction)thirdPartyApplicationClick:(UIButton *)sender {NSString *url=@"cmj://myparams";[self openUrl:url];
}
就像调用系统应用一样,协议后面可以传递一些参数(例如上面传递的myparams),这样一来在应用中可以在AppDelegate的-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation代理方法中接收参数并解析。
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{NSString *str=[NSString stringWithFormat:@"url:%@,source application:%@,params:%@",url,sourceApplication,[url host]];NSLog(@"%@",str);return YES;//是否打开
}

2. 系统服务之短信与邮件

调用系统内置的应用来发送短信、邮件相当简单,但是这么操作也存在着一些弊端:当你点击了发送短信(或邮件)操作之后直接启动了系统的短信(或邮件)应用程序,我们的应用其实此时已经处于一种挂起状态,发送完(短信或邮件)之后无法自动回到应用界面。如果想要在应用程序内部完成这些操作则可以利用iOS中的MessageUI.framework,它提供了关于短信和邮件的UI接口供开发者在应用程序内部调用。从框架名称不难看出这是一套UI接口,提供有现成的短信和邮件的编辑界面,开发人员只需要通过编程的方式给短信和邮件控制器设置对应的参数即可。

在MessageUI.framework中主要有两个控制器类分别用于发送短信(MFMessageComposeViewController)和邮件(MFMailComposeViewController),它们均继承于UINavigationController。由于两个类使用方法十分类似,这里主要介绍一下MFMessageComposeViewController使用步骤:

创建MFMessageComposeViewController对象。
设置收件人recipients、信息正文body,如果运行商支持主题和附件的话可以设置主题subject、附件attachments(可以通过canSendSubject、canSendAttachments方法判断是否支持)
设置代理messageComposeDelegate(注意这里不是delegate属性,因为delegate属性已经留给UINavigationController,MFMessageComposeViewController没有覆盖此属性而是重新定义了一个代理),实现代理方法获得发送状态。
下面自定义一个发送短信的界面演示MFMessageComposeViewController的使用:

这里写图片描述

用户通过在此界面输入短信信息点击“发送信息”调用MFMessageComposeViewController界面来展示或进一步编辑信息,点击MFMessageComposeViewController中的“发送”来完成短信发送工作,当然用户也可能点击“取消”按钮回到前一个短信编辑页面。
这里写图片描述
实现代码:

//
//  KCSendMessageViewController.m
//  iOSSystemApplication
//
//  Created by Kenshin Cui on 14/04/05.
//  Copyright (c) 2014年 cmjstudio. All rights reserved.
//#import "KCSendMessageViewController.h"
#import <MessageUI/MessageUI.h>@interface KCSendMessageViewController ()<MFMessageComposeViewControllerDelegate>@property (weak, nonatomic) IBOutlet UITextField *receivers;
@property (weak, nonatomic) IBOutlet UITextField *body;
@property (weak, nonatomic) IBOutlet UITextField *subject;
@property (weak, nonatomic) IBOutlet UITextField *attachments;@end@implementation KCSendMessageViewController
#pragma mark - 控制器视图方法
- (void)viewDidLoad {[super viewDidLoad];}#pragma mark - UI事件
- (IBAction)sendMessageClick:(UIButton *)sender {//如果能发送文本信息if([MFMessageComposeViewController canSendText]){MFMessageComposeViewController *messageController=[[MFMessageComposeViewController alloc]init];//收件人messageController.recipients=[self.receivers.text componentsSeparatedByString:@","];//信息正文messageController.body=self.body.text;//设置代理,注意这里不是delegate而是messageComposeDelegatemessageController.messageComposeDelegate=self;//如果运行商支持主题if([MFMessageComposeViewController canSendSubject]){messageController.subject=self.subject.text;}//如果运行商支持附件if ([MFMessageComposeViewController canSendAttachments]) {/*第一种方法*///messageController.attachments=...;/*第二种方法*/NSArray *attachments= [self.attachments.text componentsSeparatedByString:@","];if (attachments.count>0) {[attachments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {NSString *path=[[NSBundle mainBundle]pathForResource:obj ofType:nil];NSURL *url=[NSURL fileURLWithPath:path];[messageController addAttachmentURL:url withAlternateFilename:obj];}];}/*第三种方法*/
//            NSString *path=[[NSBundle mainBundle]pathForResource:@"photo.jpg" ofType:nil];
//            NSURL *url=[NSURL fileURLWithPath:path];
//            NSData *data=[NSData dataWithContentsOfURL:url];/***  attatchData:文件数据*  uti:统一类型标识,标识具体文件类型,详情查看:帮助文档中System-Declared Uniform Type Identifiers*  fileName:展现给用户看的文件名称*/
//            [messageController addAttachmentData:data typeIdentifier:@"public.image"  filename:@"photo.jpg"];}[self presentViewController:messageController animated:YES completion:nil];}
}#pragma mark - MFMessageComposeViewController代理方法
//发送完成,不管成功与否
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{switch (result) {case MessageComposeResultSent:NSLog(@"发送成功.");break;case MessageComposeResultCancelled:NSLog(@"取消发送.");break;default:NSLog(@"发送失败.");break;}[self dismissViewControllerAnimated:YES completion:nil];
}@end

这里需要强调一下:

.1) MFMessageComposeViewController的代理不是通过delegate属性指定的而是通过messageComposeDelegate指定的。

.2) 可以通过几种方式来指定发送的附件,在这个过程中请务必指定文件的后缀,否则在发送后无法正确识别文件类别(例如如果发送的是一张jpg图片,在发送后无法正确查看图片)。

.3) 无论发送成功与否代理方法-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result都会执行,通过代理参数中的result来获得发送状态。

其实只要熟悉了MFMessageComposeViewController之后,那么用于发送邮件的MFMailComposeViewController用法和步骤完全一致,只是功能不同。下面看一下MFMailComposeViewController的使用:

#import "KCSendEmailViewController.h"
#import <MessageUI/MessageUI.h>@interface KCSendEmailViewController ()<MFMailComposeViewControllerDelegate>
@property (weak, nonatomic) IBOutlet UITextField *toTecipients;//收件人
@property (weak, nonatomic) IBOutlet UITextField *ccRecipients;//抄送人
@property (weak, nonatomic) IBOutlet UITextField *bccRecipients;//密送人
@property (weak, nonatomic) IBOutlet UITextField *subject; //主题
@property (weak, nonatomic) IBOutlet UITextField *body;//正文
@property (weak, nonatomic) IBOutlet UITextField *attachments;//附件@end@implementation KCSendEmailViewController- (void)viewDidLoad {[super viewDidLoad];
}#pragma mark - UI事件- (IBAction)sendEmailClick:(UIButton *)sender {//判断当前是否能够发送邮件if ([MFMailComposeViewController canSendMail]) {MFMailComposeViewController *mailController=[[MFMailComposeViewController alloc]init];//设置代理,注意这里不是delegate,而是mailComposeDelegatemailController.mailComposeDelegate=self;//设置收件人[mailController setToRecipients:[self.toTecipients.text componentsSeparatedByString:@","]];//设置抄送人if (self.ccRecipients.text.length>0) {[mailController setCcRecipients:[self.ccRecipients.text componentsSeparatedByString:@","]];}//设置密送人if (self.bccRecipients.text.length>0) {[mailController setBccRecipients:[self.bccRecipients.text componentsSeparatedByString:@","]];}//设置主题[mailController setSubject:self.subject.text];//设置内容[mailController setMessageBody:self.body.text isHTML:YES];//添加附件if (self.attachments.text.length>0) {NSArray *attachments=[self.attachments.text componentsSeparatedByString:@","] ;[attachments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {NSString *file=[[NSBundle mainBundle] pathForResource:obj ofType:nil];NSData *data=[NSData dataWithContentsOfFile:file];[mailController addAttachmentData:data mimeType:@"image/jpeg" fileName:obj];//第二个参数是mimeType类型,jpg图片对应image/jpeg}];}[self presentViewController:mailController animated:YES completion:nil];}
}#pragma mark - MFMailComposeViewController代理方法
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{switch (result) {case MFMailComposeResultSent:NSLog(@"发送成功.");break;case MFMailComposeResultSaved://如果存储为草稿(点取消会提示是否存储为草稿,存储后可以到系统邮件应用的对应草稿箱找到)NSLog(@"邮件已保存.");break;case MFMailComposeResultCancelled:NSLog(@"取消发送.");break;default:NSLog(@"发送失败.");break;}if (error) {NSLog(@"发送邮件过程中发生错误,错误信息:%@",error.localizedDescription);}[self dismissViewControllerAnimated:YES completion:nil];
}@end

运行效果:
这里写图片描述

这里写图片描述

2.2. 通讯录

iOS中带有一个Contacts应用程序来管理联系人,但是有些时候我们希望自己的应用能够访问或者修改这些信息,这个时候就要用到AddressBook.framework框架。iOS中的通讯录是存储在数据库中的,由于iOS的权限设计,开发人员是不允许直接访问通讯录数据库的,必须依靠AddressBook提供的标准API来实现通讯录操作。通过AddressBook.framework开发者可以从底层去操作AddressBook.framework的所有信息,但是需要注意的是这个框架是基于C语言编写的,无法使用ARC来管理内存,开发者需要自己管理内存。下面大致介绍一下通讯录操作中常用的类型:

.1) ABAddressBookRef:代表通讯录对象,通过该对象开发人员不用过多的关注通讯录的存储方式,可以直接以透明的方式去访问、保存(在使用AddressBook.framework操作联系人时,所有的增加、删除、修改后都必须执行保存操作,类似于Core Data)等。

.2) ABRecordRef:代表一个通用的记录对象,可以是一条联系人信息,也可以是一个群组,可以通过ABRecordGetRecordType()函数获得具体类型。如果作为联系人(事实上也经常使用它作为联系人),那么这个记录记录了一个完整的联系人信息(姓名、性别、电话、邮件等),每条记录都有一个唯一的ID标示这条记录(可以通过ABRecordGetRecordID()函数获得)。

.3) ABPersonRef:代表联系人信息,很少直接使用,实际开发过程中通常会使用类型为“kABPersonType”的ABRecordRef来表示联系人(由此可见ABPersonRef其实是一种类型为“kABPersonType”的ABRecordRef)

.4)ABGroupRef:代表群组,与ABPersonRef类似,很少直接使用ABGroupRef,而是使用类型为“kABGroupType”的ABRecordRef来表示群组,一个群组可以包含多个联系人,一个联系人也同样可以多个群组。
由于通讯录操作的关键是对ABRecordRef的操作,首先看一下常用的操作通讯录记录的方法:

ABPersonCreate():创建一个类型为“kABPersonType”的ABRecordRef。ABRecordCopyValue():取得指定属性的值。

ABRecordCopyCompositeName():取得联系人(或群组)的复合信息(对于联系人则包括:姓、名、公司等信息,对于群组则返回组名称)。

ABRecordSetValue():设置ABRecordRef的属性值。注意在设置ABRecordRef的值时又分为单值属性和多值属性:单值属性设置只要通过ABRecordSetValue()方法指定属性名和值即可;多值属性则要先通过创建一个ABMutableMultiValueRef类型的变量,然后通过ABMultiValueAddValueAndLabel()方法依次添加属性值,最后通过ABRecordSetValue()方法将ABMutableMultiValueRef类型的变量设置为记录值。

ABRecordRemoveValue():删除指定的属性值。

注意:由于联系人访问时(读取、设置、删除时)牵扯到大量联系人属性,可以到ABPerson.h中查询或者直接到帮助文档“Personal Information Properties

通讯录的访问步骤一般如下:

  1. 调用ABAddressBookCreateWithOptions()方法创建通讯录对象ABAddressBookRef。
  2. 调用ABAddressBookRequestAccessWithCompletion()方法获得用户授权访问通讯录。
  3. 调用ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查询联系人信息。
  4. 调用ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查询联系人信息。
  5. 也就是说如果要修改或者删除都需要首先查询对应的联系人,然后修改或删除后提交更改。如果用户要增加一个联系人则不用进行查询,直接调用ABPersonCreate()方法创建一个ABRecord然后设置具体的属性,调用ABAddressBookAddRecord方法添加即可

下面就通过一个示例演示一下如何通过ABAddressBook.framework访问通讯录,这个例子中通过一个UITableViewController模拟一下通讯录的查看、删除、添加操作。

主控制器视图,用于显示联系人,修改删除联系人:

KCContactViewController.h

#import <UIKit/UIKit.h>/***  定义一个协议作为代理*/
@protocol KCContactDelegate
//新增或修改联系人
-(void)editPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber;
//取消修改或新增
-(void)cancelEdit;
@end@interface KCContactTableViewController : UITableViewController@end

KCContactViewController.m

#import "KCContactTableViewController.h"
#import <AddressBook/AddressBook.h>
#import "KCAddPersonViewController.h"@interface KCContactTableViewController ()<KCContactDelegate>@property (assign,nonatomic) ABAddressBookRef addressBook;//通讯录
@property (strong,nonatomic) NSMutableArray *allPerson;//通讯录所有人员@property (assign,nonatomic) int isModify;//标识是修改还是新增,通过选择cell进行导航则认为是修改,否则视为新增
@property (assign,nonatomic) UITableViewCell *selectedCell;//当前选中的单元格@end@implementation KCContactTableViewController#pragma mark - 控制器视图
- (void)viewDidLoad {[super viewDidLoad];//请求访问通讯录并初始化数据[self requestAddressBook];
}//由于在整个视图控制器周期内addressBook都驻留在内存中,所有当控制器视图销毁时销毁该对象
-(void)dealloc{if (self.addressBook!=NULL) {CFRelease(self.addressBook);}
}#pragma mark - UI事件
//点击删除按钮
- (IBAction)trashClick:(UIBarButtonItem *)sender {self.tableView.editing=!self.tableView.editing;
}#pragma mark - UITableView数据源方法- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {return 1;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.allPerson.count;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {static NSString *identtityKey=@"myTableViewCellIdentityKey1";UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:identtityKey];if(cell==nil){cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identtityKey];}//取得一条人员记录ABRecordRef recordRef=(__bridge ABRecordRef)self.allPerson[indexPath.row];//取得记录中得信息NSString *firstName=(__bridge NSString *) ABRecordCopyValue(recordRef, kABPersonFirstNameProperty);//注意这里进行了强转,不用自己释放资源NSString *lastName=(__bridge NSString *)ABRecordCopyValue(recordRef, kABPersonLastNameProperty);ABMultiValueRef phoneNumbersRef= ABRecordCopyValue(recordRef, kABPersonPhoneProperty);//获取手机号,注意手机号是ABMultiValueRef类,有可能有多条
//    NSArray *phoneNumbers=(__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(phoneNumbersRef);//取得CFArraryRef类型的手机记录并转化为NSArrarylong count= ABMultiValueGetCount(phoneNumbersRef);
//    for(int i=0;i<count;++i){
//        NSString *phoneLabel= (__bridge NSString *)(ABMultiValueCopyLabelAtIndex(phoneNumbersRef, i));
//        NSString *phoneNumber=(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, i));
//        NSLog(@"%@:%@",phoneLabel,phoneNumber);
//    }cell.textLabel.text=[NSString stringWithFormat:@"%@ %@",firstName,lastName];if (count>0) {cell.detailTextLabel.text=(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, 0));}if(ABPersonHasImageData(recordRef)){//如果有照片数据NSData *imageData= (__bridge NSData *)(ABPersonCopyImageData(recordRef));cell.imageView.image=[UIImage imageWithData:imageData];}else{cell.imageView.image=[UIImage imageNamed:@"avatar"];//没有图片使用默认头像}//使用cell的tag存储记录idcell.tag=ABRecordGetRecordID(recordRef);return cell;
}- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {if (editingStyle == UITableViewCellEditingStyleDelete) {ABRecordRef recordRef=(__bridge ABRecordRef )self.allPerson[indexPath.row];[self removePersonWithRecord:recordRef];//从通讯录删除[self.allPerson removeObjectAtIndex:indexPath.row];//从数组移除[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];//从列表删除} else if (editingStyle == UITableViewCellEditingStyleInsert) {// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view}   
}#pragma mark - UITableView代理方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{self.isModify=1;self.selectedCell=[tableView cellForRowAtIndexPath:indexPath];[self performSegueWithIdentifier:@"AddPerson" sender:self];
}#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {if([segue.identifier isEqualToString:@"AddPerson"]){UINavigationController *navigationController=(UINavigationController *)segue.destinationViewController;//根据导航控制器取得添加/修改人员的控制器视图KCAddPersonViewController *addPersonController=(KCAddPersonViewController *)navigationController.topViewController;addPersonController.delegate=self;//如果是通过选择cell进行的导航操作说明是修改,否则为添加if (self.isModify) {UITableViewCell *cell=self.selectedCell;addPersonController.recordID=(ABRecordID)cell.tag;//设置NSArray *array=[cell.textLabel.text componentsSeparatedByString:@" "];if (array.count>0) {addPersonController.firstNameText=[array firstObject];}if (array.count>1) {addPersonController.lastNameText=[array lastObject];}addPersonController.workPhoneText=cell.detailTextLabel.text;}}
}#pragma mark - KCContact代理方法
-(void)editPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber{if (self.isModify) {UITableViewCell *cell=self.selectedCell;NSIndexPath *indexPath= [self.tableView indexPathForCell:cell];[self modifyPersonWithRecordID:(ABRecordID)cell.tag firstName:firstName lastName:lastName workNumber:workNumber];[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];}else{[self addPersonWithFirstName:firstName lastName:lastName workNumber:workNumber];//通讯簿中添加信息[self initAllPerson];//重新初始化数据[self.tableView reloadData];}self.isModify=0;
}
-(void)cancelEdit{self.isModify=0;
}#pragma mark - 私有方法
/***  请求访问通讯录*/
-(void)requestAddressBook{//创建通讯录对象self.addressBook=ABAddressBookCreateWithOptions(NULL, NULL);//请求访问用户通讯录,注意无论成功与否block都会调用ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error) {if (!granted) {NSLog(@"未获得通讯录访问权限!");}[self initAllPerson];});
}
/***  取得所有通讯录记录*/
-(void)initAllPerson{//取得通讯录访问授权ABAuthorizationStatus authorization= ABAddressBookGetAuthorizationStatus();//如果未获得授权if (authorization!=kABAuthorizationStatusAuthorized) {NSLog(@"尚未获得通讯录访问授权!");return ;}//取得通讯录中所有人员记录CFArrayRef allPeople= ABAddressBookCopyArrayOfAllPeople(self.addressBook);self.allPerson=(__bridge NSMutableArray *)allPeople;//释放资源CFRelease(allPeople);}/***  删除指定的记录**  @param recordRef 要删除的记录*/
-(void)removePersonWithRecord:(ABRecordRef)recordRef{ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);//删除ABAddressBookSave(self.addressBook, NULL);//删除之后提交更改
}
/***  根据姓名删除记录*/
-(void)removePersonWithName:(NSString *)personName{CFStringRef personNameRef=(__bridge CFStringRef)(personName);CFArrayRef recordsRef= ABAddressBookCopyPeopleWithName(self.addressBook, personNameRef);//根据人员姓名查找CFIndex count= CFArrayGetCount(recordsRef);//取得记录数for (CFIndex i=0; i<count; ++i) {ABRecordRef recordRef=CFArrayGetValueAtIndex(recordsRef, i);//取得指定的记录ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);//删除}ABAddressBookSave(self.addressBook, NULL);//删除之后提交更改CFRelease(recordsRef);
}/***  添加一条记录**  @param firstName  名*  @param lastName   姓*  @param iPhoneName iPhone手机号*/
-(void)addPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber{//创建一条记录ABRecordRef recordRef= ABPersonCreate();ABRecordSetValue(recordRef, kABPersonFirstNameProperty, (__bridge CFTypeRef)(firstName), NULL);//添加名ABRecordSetValue(recordRef, kABPersonLastNameProperty, (__bridge CFTypeRef)(lastName), NULL);//添加姓ABMutableMultiValueRef multiValueRef =ABMultiValueCreateMutable(kABStringPropertyType);//添加设置多值属性ABMultiValueAddValueAndLabel(multiValueRef, (__bridge CFStringRef)(workNumber), kABWorkLabel, NULL);//添加工作电话ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL);//添加记录ABAddressBookAddRecord(self.addressBook, recordRef, NULL);//保存通讯录,提交更改ABAddressBookSave(self.addressBook, NULL);//释放资源CFRelease(recordRef);CFRelease(multiValueRef);
}/***  根据RecordID修改联系人信息**  @param recordID   记录唯一ID*  @param firstName  姓*  @param lastName   名*  @param homeNumber 工作电话*/
-(void)modifyPersonWithRecordID:(ABRecordID)recordID firstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber{ABRecordRef recordRef=ABAddressBookGetPersonWithRecordID(self.addressBook,recordID);ABRecordSetValue(recordRef, kABPersonFirstNameProperty, (__bridge CFTypeRef)(firstName), NULL);//添加名ABRecordSetValue(recordRef, kABPersonLastNameProperty, (__bridge CFTypeRef)(lastName), NULL);//添加姓ABMutableMultiValueRef multiValueRef =ABMultiValueCreateMutable(kABStringPropertyType);ABMultiValueAddValueAndLabel(multiValueRef, (__bridge CFStringRef)(workNumber), kABWorkLabel, NULL);ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL);//保存记录,提交更改ABAddressBookSave(self.addressBook, NULL);//释放资源CFRelease(multiValueRef);
}
@end

新增或修改控制器视图,用于显示一个联系人的信息或者新增一个联系人:
KCAddPersonViewController.h


#import <UIKit/UIKit.h>
@protocol KCContactDelegate;@interface KCAddPersonViewController : UIViewController@property (assign,nonatomic) int recordID;//通讯录记录id,如果ID不为0则代表修改否则认为是新增
@property (strong,nonatomic) NSString *firstNameText;
@property (strong,nonatomic) NSString *lastNameText;
@property (strong,nonatomic) NSString *workPhoneText;@property (strong,nonatomic) id<KCContactDelegate> delegate;@end

KCAddPersonViewController.m

#import "KCAddPersonViewController.h"
#import "KCContactTableViewController.h"@interface KCAddPersonViewController ()@property (weak, nonatomic) IBOutlet UITextField *firstName;
@property (weak, nonatomic) IBOutlet UITextField *lastName;
@property (weak, nonatomic) IBOutlet UITextField *workPhone;
@end@implementation KCAddPersonViewController- (void)viewDidLoad {[super viewDidLoad];[self setupUI];
}#pragma mark - UI事件
- (IBAction)cancelClick:(UIBarButtonItem *)sender {[self.delegate cancelEdit];[self dismissViewControllerAnimated:YES completion:nil];
}- (IBAction)doneClick:(UIBarButtonItem *)sender {//调用代理方法[self.delegate editPersonWithFirstName:self.firstName.text lastName:self.lastName.text workNumber:self.workPhone.text];[self dismissViewControllerAnimated:YES completion:nil];
}#pragma mark - 私有方法
-(void)setupUI{if (self.recordID) {//如果ID不为0则认为是修改,此时需要初始化界面self.firstName.text=self.firstNameText;self.lastName.text=self.lastNameText;self.workPhone.text=self.workPhoneText;}
}
@end

运行效果:
这里写图片描述

备注:1.上文中所指的以Ref结尾的对象事实上是该对象的指针(或引用),在C语言的框架中多数类型会以Ref结尾,这个类型本身就是一个指针,定义时不需要加“*”。2.通常方法中包含copy、create、new、retain等关键字的方法创建的变量使用之后需要调用对应的release方法释放。例如:使用ABPersonCreate();创建完ABRecordRef变量后使用CFRelease()方法释放。3.在与很多C语言框架交互时可以都存在Obj-CC语言类型之间的转化(特别是Obj-C和Core Foundation框架中的一些转化),此时可能会用到桥接,只要在强转之后前面加上”__bridge”即可,经过桥接转化后的类型不需要再去手动维护内存,也就不需要使用对应的release方法释放内存。4.AddressBook框架中很多类型的创建、属性设置等都是以这个类型名开发头的方法来创建的,事实上如果大家熟悉了其他框架会发现也都是类似的,这是Apple开发中约定俗成的命名规则(特别是C语言框架)。例如:要给ABRecordRef类型的变量设置属性则可以通过ABRecordSetValue()方法完成。

AddressBookUI

使用AddressBook.framework来操作通讯录特点就是可以对通讯录有更加精确的控制,但是缺点就是面对大量C语言API稍嫌麻烦,于是Apple官方提供了另一套框架供开发者使用,那就是AddressBookUI.framework。例如前面查看、新增、修改人员的界面这个框架就提供了现成的控制器视图供开发者使用。下面是这个框架中提供的控制器视图:
1. ABPersonViewController:用于查看联系人信息(可设置编辑)。需要设置displayedPerson属性来设置要显示或编辑的联系人。
2. ABNewPersonViewController:用于新增联系人信息。
3. ABUnknownPersonViewController:用于显示一个未知联系人(尚未保存的联系人)信息。需要设置displayedPerson属性来设置要显示的未知联系人。

以上三个控制器视图均继承于UIViewController,在使用过程中必须使用一个UINavigationController进行包装,否则只能看到视图内容无法进行操作(例如对于ABNewPersonViewController如果不使用UINavigationController进行包装则没有新增和取消按钮),同时注意包装后的控制器视图不需要处理具体新增、修改逻辑(增加和修改的处理逻辑对应的控制器视图内部已经完成),但是必须处理控制器的关闭操作(调用dismissViewControllerAnimated::方法),并且可以通过代理方法获得新增、修改的联系人。下面看一下三个控制器视图的代理方法:

  1. ABPersonViewController的displayViewDelegate代理方法:
-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:
``
(ABMultiValueIdentifier)identifier:此方法会在选择了一个联系人属性后触发,四个参数分别代表:使用的控制器视图、所查看的联系人、所选则的联系人属性、该属性是否是多值属性。
2. ABNewPersonViewController的newPersonViewDelegate代理方法:-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person:点击取消或完成后触发,如果参数中的person为NULL说明点击了取消,否则说明点击了完成。无论是取消还是完成操作,此方法调用时保存操作已经进行完毕,不需要在此方法中自己保存联系人信息。
3. ABUnkownPersonViewcontroller的unkownPersonViewDelegate代理方法:-(void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person:保存此联系人时调用,调用后将此联系人返回。
-(BOOL)unknownPersonViewController:(ABUnknownPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier:选择一个位置联系人属性之后执行,返回值代表是否执行默认的选择操作(例如如果是手机号,默认操作会拨打此电话)
除了上面三类控制器视图在AddressBookUI中还提供了另外一个控制器视图ABPeoplePickerNavigationController,它与之前介绍的UIImagePickerController、MPMediaPickerController类似,只是他是用来选择一个联系人的。这个控制器视图本身继承于UINavigationController,视图自身的“组”、“取消”按钮操作不需要开发者来完成(例如开发者不用在点击取消是关闭当前控制器视图,它自身已经实现了关闭方法),当然这里主要说一下这个控制器视图的peoplePickerDelegate代理方法:
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person:选择一个联系人后执行。此代理方法实现后代理方法“-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier”不会再执行。并且一旦实现了这个代理方法用户只能选择到联系人视图,无法查看具体联系人的信息。-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker:用户点击取消后执行。-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier:选择联系人具体的属性后执行,注意如果要执行此方法则不能实现-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person代理方法,此时如果点击一个具体联系人会导航到联系人详细信息界面,用户点击具体的属性后触发此方法。

下面就看一下上面四个控制器视图的使用方法,在下面的程序中定义了四个按钮,点击不同的按钮调用不同的控制器视图用于演示:


#import "ViewController.h"
#import <AddressBookUI/AddressBookUI.h>@interface ViewController ()<ABNewPersonViewControllerDelegate,ABUnknownPersonViewControllerDelegate,ABPeoplePickerNavigationControllerDelegate,ABPersonViewControllerDelegate>@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];
}#pragma mark - UI事件
//添加联系人
- (IBAction)addPersonClick:(UIButton *)sender {ABNewPersonViewController *newPersonController=[[ABNewPersonViewController alloc]init];//设置代理newPersonController.newPersonViewDelegate=self;//注意ABNewPersonViewController必须包装一层UINavigationController才能使用,否则不会出现取消和完成按钮,无法进行保存等操作UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:newPersonController];[self presentViewController:navigationController animated:YES completion:nil];
}
//
- (IBAction)unknownPersonClick:(UIButton *)sender {ABUnknownPersonViewController *unknownPersonController=[[ABUnknownPersonViewController alloc]init];//设置未知人员ABRecordRef recordRef=ABPersonCreate();ABRecordSetValue(recordRef, kABPersonFirstNameProperty, @"Kenshin", NULL);ABRecordSetValue(recordRef, kABPersonLastNameProperty, @"Cui", NULL);ABMultiValueRef multiValueRef=ABMultiValueCreateMutable(kABStringPropertyType);ABMultiValueAddValueAndLabel(multiValueRef, @"18500138888", kABHomeLabel, NULL);ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL);unknownPersonController.displayedPerson=recordRef;//设置代理unknownPersonController.unknownPersonViewDelegate=self;//设置其他属性unknownPersonController.allowsActions=YES;//显示标准操作按钮unknownPersonController.allowsAddingToAddressBook=YES;//是否允许将联系人添加到地址簿CFRelease(multiValueRef);CFRelease(recordRef);//使用导航控制器包装UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:unknownPersonController];[self presentViewController:navigationController animated:YES completion:nil];
}
- (IBAction)showPersonClick:(UIButton *)sender {ABPersonViewController *personController=[[ABPersonViewController alloc]init];//设置联系人ABAddressBookRef addressBook=ABAddressBookCreateWithOptions(NULL, NULL);ABRecordRef recordRef= ABAddressBookGetPersonWithRecordID(addressBook, 1);//取得id为1的联系人记录personController.displayedPerson=recordRef;//设置代理personController.personViewDelegate=self;//设置其他属性personController.allowsActions=YES;//是否显示发送信息、共享联系人等按钮personController.allowsEditing=YES;//允许编辑
//    personController.displayedProperties=@[@(kABPersonFirstNameProperty),@(kABPersonLastNameProperty)];//显示的联系人属性信息,默认显示所有信息//使用导航控制器包装UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:personController];[self presentViewController:navigationController animated:YES completion:nil];
}- (IBAction)selectPersonClick:(UIButton *)sender {ABPeoplePickerNavigationController *peoplePickerController=[[ABPeoplePickerNavigationController alloc]init];//设置代理peoplePickerController.peoplePickerDelegate=self;[self presentViewController:peoplePickerController animated:YES completion:nil];
}#pragma mark - ABNewPersonViewController代理方法
//完成新增(点击取消和完成按钮时调用),注意这里不用做实际的通讯录增加工作,此代理方法调用时已经完成新增,当保存成功的时候参数中得person会返回保存的记录,如果点击取消person为NULL
-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person{//如果有联系人信息if (person) {NSLog(@"%@ 信息保存成功.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));}else{NSLog(@"点击了取消.");}//关闭模态视图窗口[self dismissViewControllerAnimated:YES completion:nil];}
#pragma mark - ABUnknownPersonViewController代理方法
//保存未知联系人时触发
-(void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person{if (person) {NSLog(@"%@ 信息保存成功!",(__bridge NSString *)(ABRecordCopyCompositeName(person)));}[self dismissViewControllerAnimated:YES completion:nil];
}
//选择一个人员属性后触发,返回值YES表示触发默认行为操作,否则执行代理中自定义的操作
-(BOOL)unknownPersonViewController:(ABUnknownPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{if (person) {NSLog(@"选择了属性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property));}return NO;
}
#pragma mark - ABPersonViewController代理方法
//选择一个人员属性后触发,返回值YES表示触发默认行为操作,否则执行代理中自定义的操作
-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{if (person) {NSLog(@"选择了属性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property));}return NO;
}
#pragma mark - ABPeoplePickerNavigationController代理方法
//选择一个联系人后,注意这个代理方法实现后属性选择的方法将不会再调用
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person{if (person) {NSLog(@"选择了%@.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));}
}
//选择属性之后,注意如果上面的代理方法实现后此方法不会被调用
//-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
//    if (person && property) {
//        NSLog(@"选择了属性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property));
//    }
//}
//点击取消按钮
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{NSLog(@"取消选择.");
}
@end

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

注意:为了让大家可以更加清楚的看到几个控制器视图的使用,这里并没有结合前面的UITableViewController来使用,事实上大家结合前面UITableViewController可以做一个完善的通讯录应用。

结语

程序下载路径:
http://download.csdn.net/detail/baihuaxiu123/9507826

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. qt 获取字符串宽度

    获取字符串宽度: QFontMetrics fontMetrics(font()); int nFontWidth = fontMetrics.width(strText);...

    2024/4/19 19:42:49
  2. tcp三次握手时,最后一个ACK丢失过程分析

    客户端发送的最后一个ACK因为某些原因丢失时: 由于server端发送syn_ack报文后进入SYN_RCVD状态,就会启动tcp重传计时器,超时时就会重新发送syn_ack报文,总共发送 net.ipv4.tcp_synack_retries次;客户端在没有收到重传的syn_ack之前,发送数据给server端,由于server端…...

    2024/4/19 7:13:44
  3. 黑白棋游戏源代码分析

    游戏规则: 在一个8x8的棋盘有两个玩家,他们轮流放入分别放入白色和黑色的棋子。 当一个玩家在空棋子的位置放入一个棋子,我们检查放入棋子的8个方向 上 下 左 右 左上 左下 右上 右下,如果这8个方向上有自己这方的棋子夹的全是对方的棋子,则对方的棋子将被吃掉,变成我方的…...

    2024/5/7 14:08:00
  4. 使用 Spring Boot 开发邮件系统

    电子邮件是因特网上使用得非常多的一种应用,它可以非常方便的使相隔很远的人进行通信,它主要的特点就是操作简单,快捷。现在的电子邮件系统以是存储与转发的模型为基础。邮件服务器接受、转发、提交及存储邮件。寄信人、收信人及他们的计算机都不用同时在线。寄信人和收信人…...

    2024/4/17 17:11:35
  5. 关于C++的内存管理

    [导语]内存管理是C最令人切齿痛恨的问题,也是C最有争议的问题,C高手从中获得了更好的性能,更大的自由,C菜鸟的收获则是一遍一遍的检查代码和对C的痛恨,但内存管理在C中无处不在,内存泄漏几乎在每个C程序中都会发生,因此要想成为C高手,内存管理一关是必须要过的,除非放…...

    2024/4/11 18:43:35
  6. PHP常见截取字符串几种方法

    substr(字符串,开始位置,截取长度)如果截取的字符串是多个字节,就会出现乱码,比如中文,适用于截取字母和数字$str = hello; echo substr($str,1,2);//输出elmb_substr(字符串,开始位置,截取长度,字符编码)适用于截取中文,按字来切分echo mb_substr(这个真的很nice,0,3,utf-…...

    2024/4/17 17:13:40
  7. TCP协议中的三次握手和四次挥手 图解、原因、状态码总结

    TCP(Transmission Control Protocol) 传输控制协议从网上找了很多资料,但是每一个都不是特别全,所以总结了一下位码即TCP标志位,有6种标示: ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急) SYN(synchronous建立联机) Sequenc…...

    2024/4/19 23:08:11
  8. C++贪吃蛇代码的阅读笔记(一):tools及point

    1. system()指令的使用首先必须包含在头文件里: #include <cstdlib>system("title C++"); //设置cmd窗口标题为C++system("color 0B"); //设置颜色,0代表背景色,B代表前景色system("date /t"); //打印当前日期system("…...

    2024/4/17 17:13:36
  9. .NET开发邮件发送功能的全面教程(含邮件组件源码)

    .NET开发邮件发送功能的全面教程(含邮件组件源码)今天,给大家分享的是如何在.NET平台中开发“邮件发送”功能。在网上搜的到的各种资料一般都介绍的比较简单,那今天我想比较细的整理介绍下: 1) 邮件基础理论知识 2) 邮件发送相关.NET类库 3) 介绍我…...

    2024/4/18 0:22:10
  10. 【DIY】用Scratch简易做出国民老公王校长“热狗”游戏(+源代码+素材)

    文章目录01-模式02-素材03-逻辑04-编程05-效果查看帮助单人模式双人模式变态模式06-网盘链接被IG刷朋友圈后,王校长吃热狗的配图瞬间火爆,民间鬼才多,各个领域都在画王校长的配图,有配图就想做点什么…那就开始吧。01-模式根据王校长当时的背景,王校长吃热狗,随后IG获胜,…...

    2024/4/17 17:13:17
  11. 解决JAVA字符串长度与数据库字符串长度不一致问题

    我先说下场景,数据库定义的长度是VARCHAR2(100),文件数据有一个字段有40个字符,UTF-8编码的非英文字符,如果在插入数据库的时候如果使用String的length()方法去获取长度大小肯定是40.这里40长度肯定是小于数据库大小100,通过了验证,但是插入数据库会出现,值过大。这里是我…...

    2024/4/19 23:41:36
  12. TCP三次握手分析以及SYN,ACK,Seq含义

    一、TCP协议传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。二、TCP协议中的标识位(位码)SYN (synchronous建立联机)ACK (acknowledgement 确认)PSH (push传送)FIN (finish结束)RST (reset…...

    2024/5/7 14:56:03
  13. 【作品】多人贪吃蛇

    【下载地址】稍后上传 【以下内容摘自试验报告,可能狗屁不通,尽情谅解】 一. 需求分析: 贪吃蛇是众所周知的益智类小游戏,但是传统的贪吃蛇通常是一个人玩的,不仅枯燥,而且玩多了就觉得没有乐趣。这也是我开发“多人贪吃蛇的目的之一”,本游戏可以选择单人模式,也可以…...

    2024/4/21 14:55:57
  14. 反垃圾邮件技术解析

    1、概述 电子邮件是最常用的网络应用之一,已经成为网络交流沟通的重要途径。但是,垃圾邮件(spam)烦恼着大多数人,近来的调查显示,93%的被调查者都对他们接收到的大量垃圾邮件非常不满。一些简单的垃圾邮件事件也造成了很有影响的安全问题。日益增加的垃圾邮件现在会造…...

    2024/4/17 17:13:16
  15. Java堆外内存:堆外内存回收方法

    一、JVM内存的分配及垃圾回收对于JVM的内存规则,应该是老生常谈的东西了,这里我就简单的说下:新生代:一般来说新创建的对象都分配在这里。年老代:经过几次垃圾回收,新生代的对象就会放在年老代里面。年老代中的对象保存的时间更久。永久代:这里面存放的是class相关的信息…...

    2024/4/17 17:12:40
  16. Appgamekit制作消消乐小游戏(附代码)# 4

    废话 突然,在一个弥漫着病毒的温馨下午,我,Xresking,放下了手中的手柄,电脑屏幕上大大的红色楷体死字像是黑暗中的血月,随着苇明弦一郎的刀光,切向我的Zn头。我惊讶的发现,我摸鱼好久了… 我闲了这么多天,也不是没有收获的,比如,我通关了只狼… 好了废话不多说,我知…...

    2024/4/17 17:13:05
  17. TCP三次握手及释放连接详解

    本文整理自: http://blog.csdn.net/oney139/article/details/8103223 http://blog.csdn.net/webnumen/article/details/1541330 一、TCP头部简介ACK :即确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。TCP报文格式中的控制位…...

    2024/4/14 18:51:27
  18. Flash内存清理、建议以及查找内存泄露的方法

    转自:http://binary-house.spaces.live.com/blog/cns!A2B1EFCF718495C4!269.entry一、Flash内存清理Flash Player的garbage collection(GC)分两种运行方式,一种是“引用计数法”(Reference Counting),一种是“标记-清除法”(Mark Sweeping)。1>、引用计数法是通过计…...

    2024/5/3 23:30:44
  19. TCP协议详解(TCP报文、三次握手、四次挥手、TIME_WAIT状态、滑动窗口、拥塞控制、粘包问题、状态转换图)

    文章目录一、TCP报文(标准长度20字节)二、三次握手1、半连接、半打开、半关闭状态2、SYN flood攻击3、为什么需要三次握手?4、在三次握手过程中,如果服务器一直收不到客户端的ack会发生什么?5、初始序列号Seq为什么要随机初始化?三、四次挥手1、为什么建立连接是三次握手…...

    2024/5/7 1:41:13
  20. 【示例代码】超萌的休闲HTML5小游戏——打地鼠

    打地鼠小游戏是位HTML5达人用10天时间完成的,他总结了在研发过程中的教训和不足:1.在5种地鼠的随机产生过程中,本不应该是同等概率出现的,但我还是同概率的。应该,甚至是一关出现几只都是计算好的。 2.框架这方面,由于之前的例子是用键盘事件的,而由于我的打地鼠游戏是鼠…...

    2024/4/18 17:42:36

最新文章

  1. 大数据API技术分享:使用API接口采集淘宝数据(商品详情丨关键词搜索丨店铺所有商品)

    使用API接口采集淘宝数据&#xff08;商品详情、关键词搜索、店铺所有商品&#xff09;是大数据领域常见的应用场景。以下是一些关于如何使用API接口进行这些操作的技术分享&#xff1a; 1. 获取API权限 首先&#xff0c;你需要在淘宝开放平台注册成为开发者&#xff0c;并创建…...

    2024/5/7 17:36:06
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. 数据结构--KMP算法

    数据结构–KMP算法 首先我在这里提出以下问题&#xff0c;一会一起进行探讨 1.什么是最长公共前后缀 2. KMP算法怎么实现对匹配原理 3. 最长公共前后缀怎么求解 KMP算法可以用来解决什么问题&#xff1f; 答&#xff1a;在字符串中匹配子串&#xff0c;也称为模式匹配 分析…...

    2024/5/5 0:48:22
  4. 【虚幻引擎】C++ slate全流程开发教程

    本套课程介绍了使用我们的虚幻C去开发我们的编辑器&#xff0c;扩展我们的编辑器&#xff0c;设置我们自定义样式&#xff0c;Slate架构设计&#xff0c;自定义我们的编辑器样式&#xff0c;从基础的Slate控件到我们的布局&#xff0c;一步步的讲解我们的的Slate基础知识&#…...

    2024/5/6 9:46:04
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/7 5:50:09
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/7 14:25:14
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57