您的位置:首页 > 移动开发 > IOS开发

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

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

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

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

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

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

[objc] view plaincopy





//设置地址

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 plaincopy





/**

* 获取地址

*

* @param recordRef 通讯录单个联系人引用

*

* @return 地址

*/

-(NSDictionary*) getAddress:(ABRecordRef)recordRef{

//1.判定

if (recordRef == nil) {

return nil;

}

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对象:参考/article/1506966.html

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的。没有改

/article/1506920.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐