ARC conversion issue with handleSearchForTerm delegate method - objective-c

since converting to ARC (automatically) i have noticed with my uisearchviewcontroller delegate an issue somewhere (i think in the first if statement below). It was changed as a result but either way my app crashes when i try to perform a search:
The current code:
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (KABrand *currentBrand in [self brands])
{
if ([currentBrand.name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
if (![searchResults containsObject:currentBrand])
[[self searchResults] addObject:currentBrand];
}
}
}
The previous code:
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (KABrand *currentBrand in [self brands])
{
if ([currentBrand.name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
if (![searchResults containsObject:currentBrand])
[[self searchResults] addObject:currentBrand];
}
}
}
}
Thanks for your help in advance!
Thanks guys

Here is one potential problem: -rangeOfString:options: may return an NSNotFound. But you are checking if the range.location != NSNotFound.
If is if the return value of -rangeOfString:options: is NSNotFound, your if statement becomes NSNotFound.location != NSNotFound. I don't believe that's allowed.

Related

Reusable method to check if all objects are unequal using valueForKey

I have been working with this method for hours. It is supposed to simply check if the attribute of the cards passed in as otherCards isEqual to the same attribute of this (self) instance of class. But I am tearing my hair out - it is giving false results
(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSArray *values = [NSArray arrayWithArray:[otherCards valueForKey:key]];
int countUnequalMatches = 0;
for (NSNumber *setValue in values) {
if (![[self valueForKey:key] isEqual:setValue]) {
countUnequalMatches++;}
}
NSLog(#"countUnequalMatches %d values count: %d", countUnequalMatches, [values count]);
if (countUnequalMatches == [values count]) return YES ?: NO;
}
It is called like this:
if ([self otherCards:otherCards allHaveUnequalAttribute:CARD_SHAPE]) {
NSLog(#"All unequal");
} else {
NSLog(#"One or more card equal");
}
I found the bug myself
if (countUnequalMatches == [values count]) return YES ?: NO;
should be:
if (countUnequalMatches == [otherCards count]) return YES ?: NO;
as I used the Set as an way to count unique items.
This is the sort of thing that predicates are very well suited to handle.
-(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K == %#", key, [self valueForKey:key]];
NSArray *equalCards = [otherCards filteredArrayUsingPredicate:pred];
return equalCards.count == 0;
}

Remove object from an array stored in a singleton

Im working with a singleton to store some data, her's the implementation
static ApplicationData *sharedData = nil;
#implementation ApplicationData
#synthesize list;
+ (id)sharedData
{
static dispatch_once_t dis;
dispatch_once(&dis, ^{
if (sharedData == nil) sharedData = [[self alloc] init];
});
return sharedData;
}
- (id)init
{
if (self = [super init])
{
list = [[NSMutableArray alloc]init];
}
return self;
}
if list have less than 3 (2<) object i the app crash with "index 0 beyond bounds for empty array"
// NSMutableArray *anArray = [[NSMutableArray alloc]initWithObjects:#"", nil];
while ([[[ApplicationData sharedData]list] lastObject] != nil)
{
File *file = [[[ApplicationData sharedData]list] lastObject];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:file.filePath isDirectory:&isDir])
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:file.fileName,file.filePath,logEnteryErrorfileNotFoundDisplayName,[formatter stringFromDate:[NSDate date]], nil] forKeys:[NSArray arrayWithObjects:logShredFileName,logShredFilePath,logShredStatue,logShredDate, nil]];
[logArray addObject:tmpDic];
errorOccured = YES;
[[[ApplicationData sharedData]list] removeLastObject];
continue;
}
... other code
}
if i use the anArray that work perfectly.
what is the problem ?
That's totally weird, you've probably did something else to achieve this. Why don't you use - (void)removeAllObjects?
Maybe you remove objects in the while cycle the last line, ie:
while ([[[ApplicationData sharedData]list] count] != 0)
{
// remove object from list
// ...
[[[ApplicationData sharedData]list] removeLastObject];
}
And just a note, you don't need to check if (sharedData == nil) in sharedData as far as it's guaranteed to be executed only once. (unless you do something outside to your static variable, but that's not how it's supposed to be done I believe)

search method not working after second hit

I've a problem with my UISearchDisplayController, the search is not working properly.
This is my code:
- (void)filterContentForSearchText:(NSString*)searchText
scope:(NSString*)scope
{
[self.searchResults removeAllObjects];
for (int i = 0; i < [temp_category count]; i++) {
BOOL foundResult = FALSE;
if ([[temp_category objectAtIndex:i] rangeOfString:searchText].location != NSNotFound) {
foundResult = TRUE;
}
if ([[price_producttitle objectAtIndex:i] rangeOfString:searchText].location != NSNotFound) {
foundResult = TRUE;
}
if ([[price_type objectAtIndex:i] rangeOfString:searchText].location != NSNotFound) {
foundResult = TRUE;
}
if ([[price_description objectAtIndex:i] rangeOfString:searchText].location != NSNotFound) {
foundResult = TRUE;
}
if (foundResult) {
NSNumber *result = [NSNumber numberWithInt:i];
if ([self searchResults] == nil) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release];
}
[searchResults addObject:result];
}
}
NSLog (#"array = %i", [searchResults count]);
NSLog(#"%#",searchResults);
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text]
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:searchOption]];
return YES;
}
But I'm still confused, because when I start a search with the first letter, it gives the correct hits. But when I enter the second letter, it only shows one result (while there are more, as far as I know from my data sample). I'm doing something incorrectly. I think it has something to do with when the user enters text, but I'm confused which method I should use.
The code I now have is a combination of:
this tutorial and
this SO question.
Can someone give me a hint in the good direction? Displaying the results is fine, only this aspect bothers me. I think it has something to do with firing the method and [self.searchResults removeAllObjects];.
I would like to add my code, but the thing is that I still use the code I have above, but I'm manually implementing the UISearchBar (which I found somewhere else in a tutorial) instead of using SearchDisplayController. I also had difficulties with the navigationbar which dissappears when using SearchDisplayController, which gave me enough reason to implement it myself instead of using SearchDisplayController. It gives you more freedom.
At first it seemed a lot of work, so I choose to use SearchDisplayController, but I really advise anyone who needs some modification, or who wants more freedom, please do it manually with UISearchBar and a UITableView :)

NSDictionary in NSArray search

I'm having a problem with some search code. I have an NSMutableArray, called searchedData, that contains NSDictionaries (one per object). Here's the search code I have now:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects]; // remove all data that belongs to previous search
if([searchText isEqualToString:#""]||searchText==nil) {
[tableView reloadData];
return;
}
int i = 0;
while (i < [dataSource count]) {
NSDictionary *coolDict = [searchedData objectAtIndex:i];
NSString * title = [coolDict objectForKey:#"TITLE"];
NSString * authorString = [coolDict objectForKey:#"AUTHOR"];
NSRange titleRange = [[title lowercaseString] rangeOfString:[searchText lowercaseString]];
NSRange authorRange = [[authorString lowercaseString] rangeOfString:[searchText lowercaseString]];
if (titleRange.location != NSNotFound || authorRange.location != NSNotFound)
[tableData addObject:title];
i++;
}
[tableView reloadData];
}
It finds the number of entries, then goes to each entry, finds the objects for keys "TITLE" and "AUTHOR", and then displays entries in the UITableView if there is a match.
The problem is that it never displays anything, even if there is a match.
I know the table / dictionaries are not null (I have NSLogged it) so I don't know why it's not working.
You forgot to reload the tableView having added the relevant data to the datasource.
I have improved the coding of the method a bit, and added the missing line.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects];// remove all data that belongs to previous search
if (![searchText isEqualToString:#""] || searchText != nil) {
int i = 0;
while (i < [dataSource count]) {
NSDictionary *coolDict = [searchedData objectAtIndex:i];
NSString * title = [coolDict objectForKey:#"TITLE"];
NSString * authorString = [coolDict objectForKey:#"AUTHOR"];
NSRange titleRange = [[title lowercaseString] rangeOfString:[searchText lowercaseString]];
NSRange authorRange = [[authorString lowercaseString] rangeOfString:[searchText lowercaseString]];
if(titleRange.location != NSNotFound || authorRange.location != NSNotFound)
[tableData addObject:title];
i++;
}
}
[tableView reloadData];
}

UITableView and SearchBar problem

I'm trying to add a Search bar to my UITableView. I followed this tutorial: http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html.
I'm getting this error if I type a letter in the search box: Rooster(10787,0xa05ed4e0) malloc: *** error for object 0x3b5f160: double free
*** set a breakpoint in malloc_error_break to debug.
This error occurs here:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self handleSearchForTerm:searchString];
return YES;
}
(on the second line)
- (void)handleSearchForTerm:(NSString *)searchTerm {
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release];
}
//Empty the searchResults array
[[self searchResults] removeAllObjects];
//Check if the searchTerm doesn't equal zero...
if ([[self savedSearchTerm] length] != 0) {
//Search the whole tableList (datasource)
for (NSString *currentString in tableList) {
NSString *klasString = [[NSString alloc] init];
NSInteger i = [[leerlingNaarKlasList objectAtIndex:[tableList indexOfObject:currentString]] integerValue];
if(i != -1) {
klasString = [klassenList objectAtIndex:(i - 1)];
}
//Check if the string matched or the klas (group of school)
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound ||
[klasString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound) {
//Add to results
[[self searchResults] addObject:currentString];
//Save the klas (group of school). It has the same index as the result (lastname)
NSString *strI = [[NSString alloc] initWithFormat:#"%i", i];
[[self searchResultsLeerlingNaarKlas] addObject:strI];
[strI release];
}
[klasString release];
}
}
}
Can someone help me out?
Regards,
Dodo
The double free error means you have released an object more than needed. Here the suspicious object is klasString.
From your code:
NSString *klasString = [[NSString alloc] init];
...
if(i != -1) {
klasString = [klassenList objectAtIndex:(i - 1)];
}
...
[klasString release];
The assignment inside the if statement
loses reference to the newly allocated NSString, introducing a memory leak
makes the later release apply to the object from klassenList. When klassenList releases its elements, a double free error will occur.