iOS 修改通讯录联系人地址(address)崩溃原因分析

目前项目中需要对iOS系统通讯录进行读取,修改操作。在进行对地址修改的时候,出现了一个奇怪现象:

● 如果contact没有address字段(或者一个全新的contact),对它的address进行修改是可以成功的,

● 如果这个人有过address字段,此时对它就行修改就崩溃。控制台打出:

*** -[CFString release]: message sent to deallocated instance 0x81d26f0

这应该是一个僵尸对象,重复释放某一个对象。首先我对修改通讯录的代码进行检查,但是没发现问题,下面是代码

[objc] view plaincopy

  1. //设置地址
  2. ABMutableMultiValueRef multiAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
  3. for (PhoneTypePair* p in contact.addressArr) {
  4. //内容判断空
  5. if ([p.content length]==0) {
  6. continue;
  7. }
  8. NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
  9. //把地址只写入street字段中
  10. [addressDictionary setObject:[p.content mutableCopy] forKey:(NSString *)kABPersonAddressStreetKey];
  11. [addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressCityKey];
  12. [addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressStateKey];
  13. [addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressZIPKey];
  14. [addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressCountryCodeKey];
  15. //将字典放入多值对象中
  16. if ([p.type isEqualToString:kAddressType_Work]) {
  17. ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), kABWorkLabel, NULL);
  18. }else if ([p.type isEqualToString:kAddressType_Home]){
  19. ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), kABHomeLabel, NULL);
  20. }else{
  21. ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), kABOtherLabel, NULL);
  22. }
  23. }
  24. ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, NULL);
  25. ABAddressBookAddRecord(addressBook, person, nil);
  26. ABAddressBookSave(addressBook, NULL);
  27. if (multiAddress) {
  28. CFRelease(multiAddress);
  29. }

程序崩溃在    ABAddressBookSave(addressBook, NULL);  百思不得其解,google上查阅了很多资料,看看是不是“多值”的对象使用错了,还是代码顺序的问题。都没有结果。

后来,我想起来了Instruments这个工具,可以查看僵尸对象。立即起profile。结果如下:

Zombie的地方是ABCMultiValueDestroy。但是,我注意到了AddressBookEngine的getAddress:函数。突然我恍然大悟,应该读取的时候CF和OC对象转换的问题。随机,我打开网址,转向ARC说明

__bridge只做类型转换,但是不修改对象(内存)管理权;
__bridge_retained(也可以使用CFBridgingRetain)将Objective-C的对象转换为Core Foundation的对象,同时将对象(内存)的管理权交给我们,后续需要使用CFRelease或者相关方法来释放对象;
__bridge_transfer(也可以使用CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将对象(内存)的管理权交给ARC。

那么问题,应该就在读取address的地方了:看代码

[objc] view plaincopy

  1. /**
  2. *  获取地址
  3. *
  4. *  @param recordRef 通讯录单个联系人引用
  5. *
  6. *  @return 地址
  7. */
  8. -(NSDictionary*) getAddress:(ABRecordRef)recordRef{
  9. //1.判定
  10. if (recordRef == nil) {
  11. return nil;
  12. }
  13. NSMutableArray  *addressHome  = [[NSMutableArray alloc]init];
  14. NSMutableArray  *addressWork  = [[NSMutableArray alloc]init];
  15. NSMutableArray  *other   = [[NSMutableArray alloc]init];
  16. //2.创建字典,获取多键值列表
  17. NSMutableDictionary *multiValueDic = [[NSMutableDictionary alloc] initWithCapacity:1];
  18. ABMultiValueRef multiValueArr = ABRecordCopyValue(recordRef, kABPersonAddressProperty);
  19. //3.将多值,封装到字典中。
  20. int count = multiValueArr ? ABMultiValueGetCount(multiValueArr) : 0 ;
  21. if (count > 0) {
  22. count = (count <= kMaxAddressNumber?count:kMaxAddressNumber);
  23. for(int i = 0; i < count; i++) {
  24. @autoreleasepool {
  25. //lable
  26. //注意桥接,将CF对象转成OC对象。ARC下,自动释放OC对象:参考http://blog.csdn.net/hherima/article/details/16356577
  27. NSString* label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(multiValueArr,i));
  28. //value
  29. CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(multiValueArr, i);
  30. NSString* street  =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressStreetKey));
  31. NSString* city     =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressCityKey));
  32. NSString* country  =CFBridgingRelease(CFDictionaryGetValue(dict, kABPersonAddressCountryKey));
  33. //CFRelease(dict);//应该删除
  34. NSString *syntheticAddress = [NSString stringWithFormat:@"%@%@%@"
  35. ,(street?street:@"")
  36. ,(city?city:@"")
  37. ,(country?country:@"")];
  38. if (label == nil || [label isEqualToString:@"_$!<Home>!$_"]){
  39. [addressHome addObject:syntheticAddress];
  40. }
  41. else if([label isEqualToString:@"_$!<Work>!$_"]){
  42. [addressWork addObject:syntheticAddress];
  43. }
  44. else{
  45. [other addObject:syntheticAddress];
  46. }
  47. }
  48. }
  49. [multiValueDic setObject:addressHome forKey:@(EAdressBookType_AddressHome)];
  50. [multiValueDic setObject:addressWork forKey:@(EAdressBookType_AddressWork)];
  51. [multiValueDic setObject:other forKey:@(EAdressBookType_AddressOther)];
  52. }
  53. //4.释放CF对象
  54. if (NULL != multiValueArr) {
  55. CFRelease(multiValueArr);
  56. multiValueArr = NULL;
  57. }
  58. return multiValueDic;
  59. }

在找到具体问题之前,我做了一个假设。如果从一开始就此函数return掉,如果不崩溃,说明就是后续代码的问题。果不其然!

问题就出在:

CFRelease(dict);

由于我已经使用了CFBridgingRelease,说明不需要在releasedict这个对象了。主要是上面的代码,是从网上copy的。没有改

http://blog.csdn.net/hherima/article/details/41594273

时间: 2024-11-10 02:44:53

iOS 修改通讯录联系人地址(address)崩溃原因分析的相关文章

CSipIm断网重连崩溃原因分析

断网重连之后的操作流程 拨打电话 ->调用SipService.makeCallOptions(),先重启一遍SipService保证PjSipService的各参数正常,尤其是create参数置为true:再发送broadcast处理拨号信息 ->SipService中的Receiver接收到broadcast,在内部用全局Handler接收SipRunnable执行底层拨号操作 原因分析 此时由于Handler从旧的MessagePool中获取到的消息赋值之后,SipService才执行完

IOS获取系统通讯录联系人信息

先导入AddressBook.framework先 然后引用  #import <AddressBook/AddressBook.h> 一.权限注册 随着apple对用户隐私的越来越重视,IOS系统的权限设置也更加严格,在获取系统通讯录之前,我们必须获得用户的授权.权限申请代码示例如下: #pragma mark - 注册权限 - (void)contacts { //这个变量用于记录授权是否成功,即用户是否允许我们访问通讯录 int __block tip = 0; //声明一个通讯簿的引用

iOS.访问通讯录.02.写入联系人

一.创建联系人 1.创建联系人记录 ABRecord person = ABPersonCreate(); 2.保存或修改单值属性 bool ABRecordSetValue( ABRecordRef record, ABPropertyID property, CFTypeRef value, CFErrorRef *error ); 例子: CFError error = NULL; // 保存姓名 ABRecordSetValue(person,kABPersonFirstNameProp

iOS.访问通讯录.01.读取联系人信息

1.相关函数介绍 1.创建通讯录对象函数 ABAddressBookRef ABAddressBookCreateWithOptions( CFDictionaryRef options, CFErrorRef *error ); 例子: CFErrorRef error = NULL; ABAdressBookRef addressBook = ABAdressBookCreateWithOptions(NULL,&error); ABAddressBookRequestAccessWithC

iOS应用崩溃日志分析

转自raywenderlich 作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果你跟我一样是个完美主义者,你肯定想将应用做到尽善尽美.于是你打开代码准备修复闪退的问题……但是,从何处着手呢? 这时iOS崩溃日志派上用场了.在大多数情况下,你能从中了解到关于闪退的详尽.有用的信息. 通过本教程,你将学习到一些常见的崩溃日志案例,以及如何从开发设备和

iOS 应用崩溃日志分析

通过本教程,你将学习到一些常见的崩溃日志案例,以及如何从开发设备和iTunes Connect上获取崩溃日志文件.你还将学习到符号化( symbolication),从日志追踪到代码 .你还将学习调试一个在待定情况下会闪退的应用. 让我们开始动手吧! 什么是崩溃日志,从哪里能得它? iOS设备上的应用闪退时,操作系统会生成一个崩溃报告,也叫崩溃日志,保存在设备上. 崩溃日志上有很多有用的信息,包括应用是什么情况下闪退的.通常,上面有每个正在执行线程的完整堆栈跟踪信息,所以你能从中了解到闪退发生时

iOS应用崩溃日志分析-备用

作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果你跟我一样是个完美主义者,你肯定想将应用做到尽善尽美.于是你打开代码准备修复闪退的问题……但是,从何处着手呢? 这时iOS崩溃日志派上用场了.在大多数情况下,你能从中了解到关于闪退的详尽.有用的信息. 通过本教程,你将学习到一些常见的崩溃日志案例,以及如何从开发设备和iTunes Connect上获

【转】iOS应用崩溃日志分析

作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果你跟我一样是个完美主义者,你肯定想将应用做到尽善尽美.于是你打开代码准备修复闪退的问题……但是,从何处着手呢? 这时iOS崩溃日志派上用场了.在大多数情况下,你能从中了解到关于闪退的详尽.有用的信息. 通过本教程,你将学习到一些常见的崩溃日志案例,以及如何从开发设备和iTunes Connect上获

利用AddressBook.framework框架获取iOS系统通讯录数据

此方法是使用AddressBook.framework框架获取通讯录信息 第一步.在info.plist文件里面配置访问权限 第二步.导入头文件 1 #import <AddressBook/AddressBook.h> 2 #import <AddressBookUI/AddressBookUI.h> 第三步.获取通讯录数据 1 - (void)getSystemAddress{ 2 3 //新建一个通讯录类 4 ABAddressBookRef addressBooks = n