相关文章推荐
威武的登山鞋  ·  thymeleaf th:if ...·  7 月前    · 
爱旅游的盒饭  ·  PyPDF2 ...·  8 月前    · 
酒量小的小蝌蚪  ·  Configure ...·  1 年前    · 

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

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

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

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

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

[objc] view plain copy 在CODE上查看代码片 派生到我的代码片
  • //设置地址
  • ABMutableMultiValueRef multiAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
  • for (PhoneTypePair* p in contact .addressArr) {
  • //内容判断空
  • if ([p .content length]== 0) {
  • continue;
  • NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
  • //把地址只写入street字段中
  • [addressDictionary setObject:[p .content mutableCopy] forKey:( NSString *)kABPersonAddressStreetKey];
  • [addressDictionary setObject: @"" forKey:( NSString *)kABPersonAddressCityKey];
  • [addressDictionary setObject: @"" forKey:( NSString *)kABPersonAddressStateKey];
  • [addressDictionary setObject: @"" forKey:( NSString *)kABPersonAddressZIPKey];
  • [addressDictionary setObject: @"" forKey:( NSString *)kABPersonAddressCountryCodeKey];
  • //将字典放入多值对象中
  • if ([p .type isEqualToString:kAddressType_Work]) {
  • ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), kABWorkLabel, NULL);
  • } else if ([p .type isEqualToString:kAddressType_Home]){
  • ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), kABHomeLabel, NULL);
  • } else{
  • ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), kABOtherLabel, NULL);
  • ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, NULL);
  • ABAddressBookAddRecord(addressBook, person, nil);
  • ABAddressBookSave(addressBook, NULL);
  • if (multiAddress) {
  • CFRelease(multiAddress);
  • 程序崩溃在    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 plain copy 在CODE上查看代码片 派生到我的代码片
  • NSMutableArray  *addressHome  = [[NSMutableArray alloc]init];
  • NSMutableArray  *addressWork  = [[NSMutableArray alloc]init];
  • NSMutableArray  *other   = [[NSMutableArray alloc]init];
  • //2.创建字典,获取多键值列表
  • NSMutableDictionary *multiValueDic = [[NSMutableDictionary alloc] initWithCapacity: 1];
  • ABMultiValueRef multiValueArr = ABRecordCopyValue(recordRef, kABPersonAddressProperty);
  • //3.将多值,封装到字典中。
  • int count = multiValueArr ? ABMultiValueGetCount(multiValueArr) : 0 ;
  • if (count > 0) {
  • count = (count <= kMaxAddressNumber?count:kMaxAddressNumber);
  • for( int i = 0; i < count; i++) {
  • @autoreleasepool {
  • //lable
  • //注意桥接,将CF对象转成OC对象。ARC下,自动释放OC对象:参考http://blog.csdn.net/hherima/article/details/16356577
  • NSString* label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(multiValueArr,i));
  • //value
  • CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(multiValueArr, i);
  • NSString* street  =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressStreetKey));
  • NSString* city     =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressCityKey));
  • NSString* country  =CFBridgingRelease(CFDictionaryGetValue(dict, kABPersonAddressCountryKey));
  • //CFRelease(dict);//应该删除
  • NSString *syntheticAddress = [NSString stringWithFormat: @"%@%@%@"
  • ,(street?street: @"")
  • ,(city?city: @"")
  • ,(country?country: @"")];
  • if (label == nil || [label isEqualToString: @"_$!<Home>!$_"]){
  • [addressHome addObject:syntheticAddress];
  • else if([label isEqualToString: @"_$!<Work>!$_"]){
  • [addressWork addObject:syntheticAddress];
  • else{
  • [other addObject:syntheticAddress];
  • [multiValueDic setObject:addressHome forKey:@(EAdressBookType_AddressHome)];
  • [multiValueDic setObject:addressWork forKey:@(EAdressBookType_AddressWork)];
  • [multiValueDic setObject:other forKey:@(EAdressBookType_AddressOther)];
  • //4.释放CF对象
  • if ( NULL != multiValueArr) {
  • CFRelease(multiValueArr);
  • multiValueArr = NULL;
  • return multiValueDic;
  • 在找到具体问题之前,我做了一个假设。如果从一开始就此函数return掉,如果不崩溃,说明就是后续代码的问题。果不其然!

    问题就出在:

    CFRelease(dict);

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

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