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


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

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

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

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

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

    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的地方了:看代码

    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.                   
    29.                 //value  
    30.                 CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(multiValueArr, i);  
    31.                 NSString* street  =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressStreetKey));  
    32.                 NSString* city     =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressCityKey));  
    33.                 NSString* country  =CFBridgingRelease(CFDictionaryGetValue(dict, kABPersonAddressCountryKey));  
    34.                 //CFRelease(dict);//应该删除  
    35.                   
    36.                 NSString *syntheticAddress = [NSString stringWithFormat:@"%@%@%@"  
    37.                                               ,(street?street:@"")  
    38.                                               ,(city?city:@"")  
    39.                                               ,(country?country:@"")];  
    40.                   
    41.                 if (label == nil || [label isEqualToString:@"_$!<Home>!$_"]){  
    42.                     [addressHome addObject:syntheticAddress];  
    43.                 }  
    44.                 else if([label isEqualToString:@"_$!<Work>!$_"]){  
    45.                     [addressWork addObject:syntheticAddress];  
    46.                 }  
    47.                 else{  
    48.                     [other addObject:syntheticAddress];  
    49.                 }  
    50.   
    51.             }  
    52.         }  
    53.         [multiValueDic setObject:addressHome forKey:@(EAdressBookType_AddressHome)];  
    54.         [multiValueDic setObject:addressWork forKey:@(EAdressBookType_AddressWork)];  
    55.         [multiValueDic setObject:other forKey:@(EAdressBookType_AddressOther)];  
    56.     }  
    57.     //4.释放CF对象  
    58.     if (NULL != multiValueArr) {  
    59.         CFRelease(multiValueArr);  
    60.         multiValueArr = NULL;  
    61.     }  
    62.   
    63.     return multiValueDic;  
    64. }  

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

    问题就出在:

     CFRelease(dict);

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

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

  • 相关阅读:
    iOS10 的适配问题,你遇到了吗?导航栏标题和返回按钮神奇的消失了
    如何在获取不到第一响应者控件时移除键盘
    类名与字符串的互转
    clang format 官方文档自定义参数介绍(中英文)
    clang format 自定义样式常用参数说明
    Xcode 设置代码不自动换行
    企业项目如何打包成.ipa文件
    多个过渡动画效果
    压栈过渡动画
    底部不规则导航栏2
  • 原文地址:https://www.cnblogs.com/geek6/p/4149929.html
Copyright © 2020-2023  润新知