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
● 如果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
相关文章推荐
- iOS 修改通讯录联系人地址(address)崩溃原因分析
- iOS错误报告中关于崩溃地址的分析
- iOS友盟崩溃地址解析 通过dSYM文件分析定位线上 APP crash问题
- iOS系统通讯录授权,获取,修改,创建联系人
- iOS错误报告中关于崩溃地址的分析
- ios 选择联系人 发短信 通讯录开发 addressUI
- ios获得通讯录中联系人的所有属性
- 无法分析从服务器收到的消息。之所以出现此错误,常见的原因是: 在通过调用 Response.Write() 修改响应时,将启用响应筛选器、HttpModule 或服务器跟踪
- 无法分析从服务器收到的消息。之所以出现此错误,常见的原因是: 在通过调用 Response.Write() 修改响应时,将启用响应筛选器、HttpModule 或服务器跟踪。
- IIS和服务器崩溃原因分析
- 理解与分析ios应用的崩溃报告
- Unable to handle kernel NULL pointer dereference at virtual address-----------原因分析 ,及解决办法
- page_address()函数分析--如何通过page取得虚拟地址
- 虽然能通过minidump找到崩溃地址,但仍然不清楚崩溃原因!如何办?
- IOS 获得通讯录中联系人的所有属性
- ios开发之获得手机通讯录中所有联系人的属性
- page_address()函数分析--如何通过page取得虚拟地址
- 开源WebMail客户端Intouch2.2beta导致IE6崩溃的原因和修改方法
- 无法分析从服务器收到的消息。之所以出现此错误,常见的原因是: 在通过调用 Response.Write() 修改响应时,将启用响应筛选器、HttpModule 或服务器跟踪。
- page_address()函数分析--如何通过page取得虚拟地址