I've caught an error:
Fatal Exception: NSRangeException
*** -[__NSArrayI objectAtIndex:]: index 1330 beyond bounds [0 .. 1329]
At string
NSArray *currentABRecords = [ApplicationPhoneDirectory records];
ApplicationPhoneDirectory is NSObject with unique contacts (dict and array).
But I think the error is about working with memory, how can I manage it more properly to avoid appearance of such a problem?
Or, in case if I'm incorrect I can put records part in there.
upd:
static NSArray *RecordsForABInterval(UInt32 start, UInt32 end, CFArrayRef allPeople) {
NSMutableArray *peoples = [NSMutableArray arrayWithCapacity:(end - start) * 2];
for (int i = (int) start; i < end; i++) {
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
NSString *firstName = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
NSString *fullName = [NSString stringWithFormat:#"%# %#",
firstName.length > 0 ? firstName : #"",
lastName.length > 0 ? lastName : #""];
fullName = [fullName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
ABMutableMultiValueRef phoneList = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFIndex phonesCount = ABMultiValueGetCount(phoneList);
for (int j = 0; j < phonesCount; ++j) {
CFTypeRef ABphone = ABMultiValueCopyValueAtIndex(phoneList, j);
NSString *phone = (NSString *)CFBridgingRelease(ABphone);
NSString *phoneInSMFormat = [phone phoneNumberInSMFormat];
if ([phoneInSMFormat isValidSMPhoneNumber]) {
NSDictionary *record = #{
#"name" : fullName,
#"phone" : phoneInSMFormat,
#"original" : phone
};
[peoples addObject:record];
}
}
if (phoneList != NULL) {
CFRelease(phoneList);
}
}
return peoples;
}
+ (NSArray *)records {
const int MaxAutoreleaseStep = 500;
NSMutableArray *records = [NSMutableArray array];
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
__block BOOL accessGranted = NO;
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
dispatch_async(dispatch_get_main_queue(), ^{
accessGranted = granted;
});
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
accessGranted = YES;
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
if (addressBook != NULL) {
CFRelease(addressBook);
}
return nil;
}
// TODO: review this logic - should it be a single call to AB?
if (addressBook != NULL) {
CFRelease(addressBook);
}
if (accessGranted) {
ABAddressBookRef addressbook = ABAddressBookCreateWithOptions(NULL, &error);
if (error != NULL) {
if (addressbook != NULL) {
CFRelease(addressbook);
}
return nil;
}
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressbook);
CFIndex peopleCount = ABAddressBookGetPersonCount(addressbook);
for (int start = 0; start < peopleCount; start += MaxAutoreleaseStep) {
#autoreleasepool {
int end = start + MaxAutoreleaseStep;
NSArray *contactListCut = RecordsForABInterval((UInt32) start, (UInt32) MIN((int)peopleCount, end), allPeople);
[records addObjectsFromArray:contactListCut];
}
}
if (allPeople != NULL) {
CFRelease(allPeople);
}
if (addressbook != NULL) {
CFRelease(addressbook);
}
}
return records;
}
upd2:
- (NSString *)phoneNumberInSMFormat {
NSString *phone = [self numericPresentation];
if (phone.length > 0 && [phone characterAtIndex:0] == '8') {
NSMutableString *modifiedString = [phone mutableCopy];
[modifiedString replaceCharactersInRange:NSMakeRange(0, 1) withString:#"7"];
phone = modifiedString;
}
return phone;
}
- (BOOL)isValidSMPhoneNumber {
static NSCharacterSet *characterSet = nil;
if (characterSet == nil) {
characterSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
}
if ([self rangeOfCharacterFromSet:characterSet].location != NSNotFound) {
return NO;
}
if (self.length < 7 || self.length > 12) {
return NO;
}
return [self characterAtIndex:0] != '8';
}
i use linphone SDK in my application. When i start to run app crash happening immediately.
After app start it's call [Fastaddreesbook init] which called [FastAddreesBook reload] which called [FastAddreesBook loadData] which called [FastAddreesBook normalizeSipURI] and exc_bad_access Crash happen in this method:
LinphoneAddress* linphoneAddress = linphone_core_interpret_url([LinphoneManager getLc], [address UTF8String]);
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
/* FastAddressBook.h
*
* Copyright (C) 2011 Belledonne Comunications, Grenoble, France
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef __IPHONE_9_0
#import <Contacts/Contacts.h>
#endif
#import "FastAddressBook.h"
#import "LinphoneManager.h"
#import "BundleLocalData.h"
#import "AppUtil.h"
#import "WebserviceUtil.h"
#import "ContactEntry.h"
#import "ContactsViewController.h"
#implementation FastAddressBook
static void sync_address_book (ABAddressBookRef addressBook, CFDictionaryRef info, void *context);
+ (NSString*)getContactDisplayName:(ABRecordRef)contact {
NSString *retString = nil;
if (contact) {
CFStringRef lDisplayName = ABRecordCopyCompositeName(contact);
if(lDisplayName != NULL) {
retString = [NSString stringWithString:(NSString*)lDisplayName];
CFRelease(lDisplayName);
}
}
return retString;
}
+ (UIImage*)squareImageCrop:(UIImage*)image
{
UIImage *ret = nil;
// This calculates the crop area.
float originalWidth = image.size.width;
float originalHeight = image.size.height;
float edge = fminf(originalWidth, originalHeight);
float posX = (originalWidth - edge) / 2.0f;
float posY = (originalHeight - edge) / 2.0f;
CGRect cropSquare = CGRectMake(posX, posY,
edge, edge);
CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, cropSquare);
ret = [UIImage imageWithCGImage:imageRef
scale:image.scale
orientation:image.imageOrientation];
CGImageRelease(imageRef);
return ret;
}
+ (UIImage*)getContactImage:(ABRecordRef)contact thumbnail:(BOOL)thumbnail {
UIImage* retImage = nil;
if (contact && ABPersonHasImageData(contact)) {
CFDataRef imgData = ABPersonCopyImageDataWithFormat(contact, thumbnail?
kABPersonImageFormatThumbnail: kABPersonImageFormatOriginalSize);
retImage = [UIImage imageWithData:(NSData *)imgData];
if(imgData != NULL) {
CFRelease(imgData);
}
if (retImage != nil && retImage.size.width != retImage.size.height) {
[LinphoneLogger log:LinphoneLoggerLog format:#"Image is not square : cropping it."];
return [self squareImageCrop:retImage];
}
}
return retImage;
}
- (ABRecordRef)getContact:(NSString*)address {
#synchronized (addressBookMap){
return (ABRecordRef)addressBookMap[address];
}
}
+ (BOOL)isSipURI:(NSString*)address {
return [address hasPrefix:#"sip:"] || [address hasPrefix:#"sips:"];
}
+ (NSString*)appendCountryCodeIfPossible:(NSString*)number {
if (![number hasPrefix:#"+"] && ![number hasPrefix:#"00"]) {
NSString* lCountryCode = [[LinphoneManager instance] lpConfigStringForKey:#"countrycode_preference"];
if (lCountryCode && lCountryCode.length>0) {
//append country code
return [lCountryCode stringByAppendingString:number];
}
}
return number;
}
+ (NSString*)normalizeSipURI:(NSString*)address {
NSString *normalizedSipAddress = nil;
LinphoneAddress* linphoneAddress = linphone_core_interpret_url([LinphoneManager getLc], [address UTF8String]);
if(linphoneAddress != NULL) {
char *tmp = linphone_address_as_string_uri_only(linphoneAddress);
if(tmp != NULL) {
normalizedSipAddress = [NSString stringWithUTF8String:tmp];
ms_free(tmp);
}
linphone_address_destroy(linphoneAddress);
}
return normalizedSipAddress;
}
+ (NSString*)normalizePhoneNumber:(NSString*)address {
NSMutableString* lNormalizedAddress = [NSMutableString stringWithString:address];
[lNormalizedAddress replaceOccurrencesOfString:#" "
withString:#""
options:0
range:NSMakeRange(0, lNormalizedAddress.length)];
[lNormalizedAddress replaceOccurrencesOfString:#"("
withString:#""
options:0
range:NSMakeRange(0, lNormalizedAddress.length)];
[lNormalizedAddress replaceOccurrencesOfString:#")"
withString:#""
options:0
range:NSMakeRange(0, lNormalizedAddress.length)];
[lNormalizedAddress replaceOccurrencesOfString:#"-"
withString:#""
options:0
range:NSMakeRange(0, lNormalizedAddress.length)];
return [FastAddressBook appendCountryCodeIfPossible:lNormalizedAddress];
}
+ (BOOL)isAuthorized {
//addme // return !ABAddressBookGetAuthorizationStatus || ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized;
return ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized;
}
- (FastAddressBook*)init {
if ((self = [super init]) != nil) {
addressBookMap = [[NSMutableDictionary alloc] init];
addressBook = nil;
[self reload];
}
self.needToUpdate = FALSE;
if ([CNContactStore class]) {
//ios9 or later
CNEntityType entityType = CNEntityTypeContacts;
if([CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined) {
CNContactStore * contactStore = [[CNContactStore alloc] init];
// nslo(#"CNContactStore requesting authorization");
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
// LOGD(#"CNContactStore authorization granted");
}];
} else if([CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized) {
// LOGD(#"CNContactStore authorization granted");
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateAddressBook:) name:CNContactStoreDidChangeNotification object:nil];
}
return self;
}
-(void) updateAddressBook:(NSNotification*) notif {
// LOGD(#"address book has changed");
self.needToUpdate = TRUE;
}
- (void) checkContactListForJogvoiceList {
// if (![BundleLocalData isLoadingJogvoiceContactList]) {
// [BundleLocalData setLoadingJogvoiceContactList:true];
int maxPhoneNumberSubmit = 200;
NSArray *lContacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSMutableDictionary *phoneNumberContactsDictionary = [[NSMutableDictionary alloc] init];
NSMutableArray *allPhoneNumberList = [[NSMutableArray alloc] init];
for (id lPerson in lContacts) {
ABRecordRef person = (ABRecordRef)lPerson;
NSArray *phoneList = [AppUtil getContactPhoneList:person];
for (NSString* phoneNumber in phoneList) {
NSMutableArray* contactList = phoneNumberContactsDictionary[phoneNumber];
if (!contactList) {
contactList = [[NSMutableArray alloc] init];
}
[contactList addObject:(__bridge ABRecordRef)person];
phoneNumberContactsDictionary[phoneNumber] = contactList;
}
[allPhoneNumberList addObjectsFromArray:phoneList];
if (allPhoneNumberList.count >= maxPhoneNumberSubmit) {
[self checkContactList:allPhoneNumberList phoneNumberContactsDictionary:phoneNumberContactsDictionary];
}
}
if (allPhoneNumberList.count > 0) {
[self checkContactList:allPhoneNumberList phoneNumberContactsDictionary:phoneNumberContactsDictionary];
}
// ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, self);
// [BundleLocalData setLoadingJogvoiceContactList:false];
// }
}
-(void) checkContactList:(NSMutableArray*)allPhoneNumberList phoneNumberContactsDictionary:(NSMutableDictionary*)phoneNumberContactsDictionary {
[WebserviceUtil apiGetUsersRegistered:[NSArray arrayWithArray:allPhoneNumberList]
success:^(AFHTTPRequestOperation *operation, id responseObject){
NSDictionary* response = responseObject;
for (id phoneNumber in allPhoneNumberList) {
NSNumber *status = response[phoneNumber];
if (status.intValue == 1) { // registered
NSArray* contactList = phoneNumberContactsDictionary[phoneNumber];
for (int index = 0; index < contactList.count; index++) {
ABRecordRef contact = (__bridge ABRecordRef) contactList[index];
[self saveContact:phoneNumber contact:contact];
}
}
}
[self saveAddressBook];
[allPhoneNumberList removeAllObjects];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// nothing
}];
}
-(void) saveContact:(NSString*)phoneNumber contact:(ABRecordRef)contact {
if(contact == NULL || phoneNumber == NULL) {
return;
}
ABMultiValueRef lMap = ABRecordCopyValue(contact, kABPersonInstantMessageProperty);
if (!lMap) {
return;
}
BOOL avafoneAlready = false;
for(int i = 0; i < ABMultiValueGetCount(lMap); ++i) {
ABMultiValueIdentifier identifier = ABMultiValueGetIdentifierAtIndex(lMap, i);
CFDictionaryRef lDict = ABMultiValueCopyValueAtIndex(lMap, i);
if(CFDictionaryContainsKey(lDict, kABPersonInstantMessageServiceKey)) {
if(CFStringCompare((CFStringRef)[LinphoneManager instance].contactSipField, CFDictionaryGetValue(lDict, kABPersonInstantMessageServiceKey), kCFCompareCaseInsensitive) == 0) {
avafoneAlready = true;
}
} else {
//check domain
LinphoneAddress* address = linphone_address_new(((NSString*)CFDictionaryGetValue(lDict,kABPersonInstantMessageUsernameKey)).UTF8String);
if (address) {
if ([[ContactSelection getSipFilter] compare:#"*" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
avafoneAlready = true;
} else {
NSString* domain = [NSString stringWithCString:linphone_address_get_domain(address)
encoding:[NSString defaultCStringEncoding]];
if ([domain compare:[ContactSelection getSipFilter] options:NSCaseInsensitiveSearch] == NSOrderedSame) {
avafoneAlready = true;
}
}
linphone_address_destroy(address);
}
}
CFRelease(lDict);
if(avafoneAlready) {
avafoneAlready = true;
break;
}
}
CFRelease(lMap);
if (avafoneAlready) {
return;
}
NSString *value = [NSString stringWithFormat:#"900%#", phoneNumber];
ContactEntry *entry = nil;
ABMultiValueRef lcMap = ABRecordCopyValue(contact, kABPersonInstantMessageProperty);
if(lcMap != NULL) {
lMap = ABMultiValueCreateMutableCopy(lcMap);
CFRelease(lcMap);
} else {
lMap = ABMultiValueCreateMutable(kABStringPropertyType);
}
ABMultiValueIdentifier index;
NSError* error = NULL;
CFStringRef keys[] = { kABPersonInstantMessageUsernameKey, kABPersonInstantMessageServiceKey};
CFTypeRef values[] = { [value copy], [LinphoneManager instance].contactSipField };
CFDictionaryRef lDict = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, NULL, NULL);
if (entry) {
index = ABMultiValueGetIndexForIdentifier(lMap, entry.identifier);
ABMultiValueReplaceValueAtIndex(lMap, lDict, index);
} else {
CFStringRef label = (CFStringRef)[NSString stringWithString:(NSString*)kABPersonPhoneMobileLabel];
ABMultiValueAddValueAndLabel(lMap, lDict, label, &index);
}
if (!ABRecordSetValue(contact, kABPersonInstantMessageProperty, lMap, (CFErrorRef*)&error)) {
[LinphoneLogger log:LinphoneLoggerLog format:#"Can't set contact with value [%#] cause [%#]", value,error.localizedDescription];
CFRelease(lMap);
} else {
if (entry == nil) {
entry = [[[ContactEntry alloc] initWithData:index] autorelease];
}
CFRelease(lDict);
CFRelease(lMap);
/*check if message type is kept or not*/
lcMap = ABRecordCopyValue(contact, kABPersonInstantMessageProperty);
lMap = ABMultiValueCreateMutableCopy(lcMap);
CFRelease(lcMap);
index = ABMultiValueGetIndexForIdentifier(lMap, entry.identifier);
lDict = ABMultiValueCopyValueAtIndex(lMap,index);
// if(!CFDictionaryContainsKey(lDict, kABPersonInstantMessageServiceKey)) {
/*too bad probably a gtalk number, storing uri*/
NSString* username = CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey);
LinphoneAddress* address = linphone_core_interpret_url([LinphoneManager getLc]
,username.UTF8String);
if(address){
char* uri = linphone_address_as_string_uri_only(address);
CFStringRef keys[] = { kABPersonInstantMessageUsernameKey, kABPersonInstantMessageServiceKey};
CFTypeRef values[] = { [NSString stringWithCString:uri encoding:[NSString defaultCStringEncoding]], [LinphoneManager instance].contactSipField };
CFDictionaryRef lDict2 = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, NULL, NULL);
ABMultiValueReplaceValueAtIndex(lMap, lDict2, index);
if (!ABRecordSetValue(contact, kABPersonInstantMessageProperty, lMap, (CFErrorRef*)&error)) {
[LinphoneLogger log:LinphoneLoggerLog format:#"Can't set contact with value [%#] cause [%#]", value,error.localizedDescription];
}
CFRelease(lDict2);
linphone_address_destroy(address);
ms_free(uri);
}
// }
CFDictionaryRef lDict = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 2, NULL, NULL);
ABMultiValueReplaceValueAtIndex(lMap, lDict, index);
CFRelease(lMap);
}
CFRelease(lDict);
}
- (void)saveAddressBook {
if( addressBook != nil ){
NSError* err = nil;
if( !ABAddressBookSave(addressBook, (CFErrorRef*)err) ){
Linphone_warn(#"Couldn't save Address Book");
}
}
}
- (void)reload {
NSLog(#"Fastadd reload first is loaded");
CFErrorRef error;
// create if it doesn't exist
if (addressBook == nil) {
addressBook = ABAddressBookCreateWithOptions(NULL, &error);
}
if (addressBook != nil) {
__weak FastAddressBook *weakSelf = self;
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (!granted) {
Linphone_warn(#"Permission for address book acces was denied: %#", [(__bridge NSError *)error description]);
return;
}
ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(weakSelf));
dispatch_async(dispatch_get_main_queue(), ^(void) {
[weakSelf loadData];
});
});
} else {
Linphone_warn(#"Create AddressBook failed, reason: %#", [(__bridge NSError *)error localizedDescription]);
}
/*
//method1
if(addressBook != nil) {
ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, self);
CFRelease(addressBook);
addressBook = nil;
}
NSError *error = nil;
addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
if(addressBook != NULL) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
ABAddressBookRegisterExternalChangeCallback (addressBook, sync_address_book, self);
[self loadData];
});
} else {
[LinphoneLogger log:LinphoneLoggerError format:#"Create AddressBook: Fail(%#)", [error localizedDescription]];
}
*/
}
- (void)loadData {
ABAddressBookRevert(addressBook);
#synchronized (addressBookMap) {
[addressBookMap removeAllObjects];
//melog
NSLog(#"Fastadd loaddata is loaded");
NSArray *lContacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (id lPerson in lContacts) {
// Phone
{
ABMultiValueRef lMap = ABRecordCopyValue((ABRecordRef)lPerson, kABPersonPhoneProperty);
if(lMap) {
for (int i=0; i<ABMultiValueGetCount(lMap); i++) {
CFStringRef lValue = ABMultiValueCopyValueAtIndex(lMap, i);
CFStringRef lLabel = ABMultiValueCopyLabelAtIndex(lMap, i);
CFStringRef lLocalizedLabel = ABAddressBookCopyLocalizedLabel(lLabel);
NSString* lNormalizedKey = [FastAddressBook normalizePhoneNumber:(NSString*)lValue];
NSString* lNormalizedSipKey = [FastAddressBook normalizeSipURI:lNormalizedKey];
if (lNormalizedSipKey != NULL) lNormalizedKey = lNormalizedSipKey;
addressBookMap[lNormalizedKey] = lPerson;
CFRelease(lValue);
if (lLabel) CFRelease(lLabel);
if (lLocalizedLabel) CFRelease(lLocalizedLabel);
}
CFRelease(lMap);
}
}
// SIP
{
ABMultiValueRef lMap = ABRecordCopyValue((ABRecordRef)lPerson, kABPersonInstantMessageProperty);
if(lMap) {
for(int i = 0; i < ABMultiValueGetCount(lMap); ++i) {
CFDictionaryRef lDict = ABMultiValueCopyValueAtIndex(lMap, i);
BOOL add = false;
if(CFDictionaryContainsKey(lDict, kABPersonInstantMessageServiceKey)) {
CFStringRef contactSipField = (CFStringRef)[LinphoneManager instance].contactSipField;
if (!contactSipField) {
contactSipField = CFStringCreateWithCString(NULL, "SIP", kCFStringEncodingMacRoman);
}
if(CFStringCompare(contactSipField, CFDictionaryGetValue(lDict, kABPersonInstantMessageServiceKey), kCFCompareCaseInsensitive) == 0) {
add = true;
}
} else {
add = true;
}
if(add) {
CFStringRef lValue = CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey);
NSString* lNormalizedKey = [FastAddressBook normalizeSipURI:(NSString*)lValue];
if(lNormalizedKey != NULL) {
addressBookMap[lNormalizedKey] = lPerson;
} else {
addressBookMap[(NSString*)lValue] = lPerson;
}
/*
NSString *lValue =
(__bridge NSString *)CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey);
NSString *lNormalizedKey = [FastAddressBook normalizeSipURI:lValue];
if (lNormalizedKey != NULL) {
[addressBookMap setObject:(__bridge id)(lPerson)forKey:lNormalizedKey];
} else {
[addressBookMap setObject:(__bridge id)(lPerson)forKey:lValue];
}
*/
}
CFRelease(lDict);
}
CFRelease(lMap);
}
}
}
CFRelease(lContacts);
}
[[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneAddressBookUpdate object:self];
}
void sync_address_book (ABAddressBookRef addressBook, CFDictionaryRef info, void *context) {
FastAddressBook* fastAddressBook = (FastAddressBook*)context;
[fastAddressBook loadData];
}
- (void)dealloc {
ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, self);
CFRelease(addressBook);
[addressBookMap release];
[super dealloc];
}
#end
P.S.:
-i use non-arc project
-zombie enable too but nothing change.
-Ivalue is will and thats because crash happening.
Debug Console:
warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available.
LinphoneAddress* linphoneAddress = linphone_core_interpret_url([LinphoneManager getLc], [address UTF8String]);
address is nil. Figure out why that is and you've got your crash source. It likely should be nil as it is probably an optional field in the original record.
That code is a bit of a mess, btw. It isn't following the standard patterns (lots of method prefixed with get, for example). It really should be modernized and have ARC enabled.
Here's the source code for the method that appears to be causing the leak.
- (void)search:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = command.callbackId;
NSArray* fields = [command argumentAtIndex:0];
NSDictionary* findOptions = [command argumentAtIndex:1 withDefault:[NSNull null]];
[self.commandDelegate runInBackground:^{
// from Apple: Important You must ensure that an instance of ABAddressBookRef is used by only one thread.
// which is why address book is created within the dispatch queue.
// more details here: http: //blog.byadrian.net/2012/05/05/ios-addressbook-framework-and-gcd/
CDVAddressBookHelper* abHelper = [[CDVAddressBookHelper alloc] init];
CDVContacts* __weak weakSelf = self; // play it safe to avoid retain cycles
// it gets uglier, block within block.....
[abHelper createAddressBook: ^(ABAddressBookRef addrBook, CDVAddressBookAccessError* errCode) {
if (addrBook == NULL) {
// permission was denied or other error - return error
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:errCode ? (int)errCode.errorCode:UNKNOWN_ERROR];
[weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
return;
}
NSArray* foundRecords = nil;
// get the findOptions values
BOOL multiple = NO; // default is false
NSString* filter = nil;
NSArray* desiredFields = nil;
if (![findOptions isKindOfClass:[NSNull class]]) {
id value = nil;
filter = (NSString*)[findOptions objectForKey:#"filter"];
value = [findOptions objectForKey:#"multiple"];
if ([value isKindOfClass:[NSNumber class]]) {
// multiple is a boolean that will come through as an NSNumber
multiple = [(NSNumber*)value boolValue];
// NSLog(#"multiple is: %d", multiple);
}
desiredFields = [findOptions objectForKey:#"desiredFields"];
// return all fields if desired fields are not explicitly defined
if (desiredFields == nil || desiredFields.count == 0) {
desiredFields = [NSArray arrayWithObjects:#"*", nil];
}
}
NSDictionary* searchFields = [[CDVContact class] calcReturnFields:fields];
NSDictionary* returnFields = [[CDVContact class] calcReturnFields:desiredFields];
NSMutableArray* matches = nil;
if (!filter || [filter isEqualToString:#""]) {
// get all records
foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
if (foundRecords && ([foundRecords count] > 0)) {
// create Contacts and put into matches array
// doesn't make sense to ask for all records when multiple == NO but better check
int xferCount = multiple == YES ? (int)[foundRecords count] : 1;
matches = [NSMutableArray arrayWithCapacity:xferCount];
for (int k = 0; k < xferCount; k++) {
CDVContact* xferContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:k]];
[matches addObject:xferContact];
xferContact = nil;
}
}
} else {
foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);
matches = [NSMutableArray arrayWithCapacity:1];
BOOL bFound = NO;
int testCount = (int)[foundRecords count];
for (int j = 0; j < testCount; j++) {
CDVContact* testContact = [[CDVContact alloc] initFromABRecord:(__bridge ABRecordRef)[foundRecords objectAtIndex:j]];
if (testContact) {
bFound = [testContact foundValue:filter inFields:searchFields];
if (bFound) {
[matches addObject:testContact];
}
testContact = nil;
}
}
}
NSMutableArray* returnContacts = [NSMutableArray arrayWithCapacity:1];
if ((matches != nil) && ([matches count] > 0)) {
// convert to JS Contacts format and return in callback
// - returnFields determines what properties to return
#autoreleasepool {
int count = multiple == YES ? (int)[matches count] : 1;
for (int i = 0; i < count; i++) {
CDVContact* newContact = [matches objectAtIndex:i];
NSDictionary* aContact = [newContact toDictionary:returnFields];
[returnContacts addObject:aContact];
}
}
}
// return found contacts (array is empty if no contacts found)
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:returnContacts];
[weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
// NSLog(#"findCallback string: %#", jsString);
if (addrBook) {
CFRelease(addrBook);
}
}];
}]; // end of workQueue block
return;
}
The specific line that is doing most of the leaking is foundRecords = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addrBook);, but this is confusing, given that the correct __bridge_transfer call is used. What's going on here?
I've been reading all questions related to SQlite encoding with no success, so I'll try to ask about my specific case.
I have an iOS app with a SQLite database prepopulated.
This SQLite has been converted from a MySQL database.
Both the SQLite and MySQL databases have UTF8 enconding (the conversion process set the default sqlite enconding to UTF8)
If I open my sqlite database with Firefox SQlite Manager I can read all the content without problems, and all UTF8 chars are shown properly, so I guess everything was converted correctly.
But, when I try to perfom a select with UTF8 chars inside, the query fails.
This is the method where I perform the queries:
- (NSArray *)performQuery:(NSString *)query
{
static sqlite3 *db;
sqlite3_stmt *resultado;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"mydatabase.sqlite"];
const char *dbpath = [writableDBPath UTF8String];
if (sqlite3_open(dbpath, &db) == SQLITE_OK)
{
printf("%s\n", [query UTF8String]);
int codigo = sqlite3_prepare_v2(db,[query UTF8String],[query length],&resultado,NULL);
if (codigo == SQLITE_OK)
{
NSMutableArray *result = [NSMutableArray array];
while (sqlite3_step(resultado) == SQLITE_ROW)
{
NSMutableArray *row = [NSMutableArray array];
for (int i = 0; i < sqlite3_column_count(resultado); i++)
{
int colType = sqlite3_column_type(resultado, i);
id value;
if (colType == SQLITE_TEXT)
{
value = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(resultado, i)];
if ([value isEqualToString:#"(null)"] || [value isEqualToString:#""] || (value == nil) || [value isKindOfClass:[NSNull class]])
{
value = #"";
}
}
else if (colType == SQLITE_INTEGER)
{
int col = sqlite3_column_int(resultado, i);
value = [NSNumber numberWithInt:col];
}
else if (colType == SQLITE_FLOAT)
{
double col = sqlite3_column_double(resultado, i);
value = [NSNumber numberWithDouble:col];
}
else if (colType == SQLITE_NULL)
{
value = [NSNull null];
}
else
{
NSLog(#"ERROR: DataBase - performQuery - Campo desconocido");
}
[row addObject:value];
}
[result addObject:row];
row = nil;
}
sqlite3_finalize(resultado);
sqlite3_close(db);
resultado = nil;
return result;
}
else
{
NSLog(#"ERROR [%d](%s): %#", codigo, sqlite3_errmsg(db), query);
}
}
else
{
NSLog(#"ERROR: No se pudo abrir la base de datos");
}
sqlite3_close(db);
db = nil;
resultado = nil;
return nil;
}
And this is the method where I call performQuery:
- (int) getIdForItem:(NSString *)item
{
NSString *query = [NSString stringWithFormat:#"SELECT item_id FROM table_items WHERE item_name=\"%#\"", item];
NSLog(#"QUERY:\n%#", query);
NSArray *answer = [self performQuery:query];
if([answer count] > 0)
return [[[answer objectAtIndex:0] objectAtIndex:0] intValue];
return -1;
}
If I make this call:
[self getIdForItem:#"Camión"];
This is the output:
QUERY:
SELECT item_id FROM table_items WHERE item_name="Camión"
SELECT item_id FROM table_items WHERE item_name="Camión"
ERROR [1](unrecognized token: ""Cami√≥n"): SELECT item_id FROM table_items WHERE item_name="Camión"
This error is produced in the method:
sqlite3_prepare_v2(db,[query UTF8String],[query length],&resultado,NULL)
So, Why is Camión interpreted as Cami√≥n in sqlite_prepare_v2? Both the query and the sqlite database are using UTF8, so I guess I'm missing something...
Well, the rmaddy's solution worked perfectly for me. What I've done is:
PerformQuery is almost the same, it receives a new argument:
- (NSArray *)performQuery:(NSString *)query withArgument:(NSString *)arg
Just after the sqlite3_prepare_v2 I've made the bind:
int codigo = sqlite3_prepare_v2(db,[query UTF8String],[query length],&resultado,NULL);
if (codigo == SQLITE_OK)
{
sqlite3_bind_text(resultado, 1, [arg UTF8String], -1, SQLITE_TRANSIENT);
NSMutableArray *result = [NSMutableArray array];
while (sqlite3_step(resultado) == SQLITE_ROW)
And finally, I call this method this way:
- (int) getIdForItem:(NSString *)item
{
NSString *query = #"SELECT item_id FROM table_items WHERE item_name=?1";
NSLog(#"QUERY:\n%#", query);
NSArray *answer = [self performQuery:query];
if([answer count] > 0)
return [[[answer objectAtIndex:0] objectAtIndex:0] intValue];
return -1;
}
I have data exported to excel it works fine.
But I have a little question
My output is exported like this:
What i would like to happen is this:
and this is my code to export:
-(void)exportCSV {
NSArray * data = [NSArray arrayWithObjects:entries,keys, nil];
NSLog(#"%#",data);
csv =[NSMutableString string];
for (NSArray * line in data) {
NSMutableArray * formattedLine = [NSMutableArray array];
for ( field in line) {
BOOL shouldQuote = NO;
NSRange r = [field rangeOfString:#","];
//fields that contain a , must be quoted
if (r.location != NSNotFound) {
shouldQuote = YES;
}
r = [field rangeOfString:#"\""];
//fields that contain a " must have them escaped to "" and be quoted
if (r.location != NSNotFound) {
field = [field stringByReplacingOccurrencesOfString:#"\"" withString:#"\"\""];
shouldQuote = YES;
}
if (shouldQuote == YES) {
[formattedLine addObject:[NSString stringWithFormat:#"\"%#\"\"%#\"", entries,keys]];
} else {
[formattedLine addObject:field];
}
}
NSString * combinedLine = [formattedLine componentsJoinedByString:#";"];
[csv appendFormat:#"%#\n", combinedLine];
NSLog(#"%#",csv);
}
}
Does the following do what you want?
Note that I have not considered quotation, I leave that up to you ;)
Also note that I assume that entries.count == keys.count
- (void)exportCSV {
NSArray *keys = #[#"T", #"R", #"RTT"];
NSArray *entries = #[#"-329180696", #"1243918297", #"-998693494"];
NSMutableString *csv = [[NSMutableString alloc] initWithCapacity:0];
for (int i = 0; i < entries.count; i++) {
[csv appendFormat:#"%#;%#\n", keys[i], entries[i]];
}
}
Output:
T;-329180696
R;1243918297
RTT;-998693494