0

I'm making an app which searches texts (enumerating through the whole text via a block method). The search controller class is supposed to compile the results into an array, which is then returned via a delegate method.

I know the search logic is working, because before I added the delegate method I was getting results, and my resultsCount integer is still logging the expected number of results. What I've changed is to call this method in a queue, and dispatch it back to the main queue as follows:

searchQueue = dispatch_queue_create("searchQueue", NULL);
dispatch_async(searchQueue, ^{

    dispatch_async(dispatch_get_main_queue(), ^ {
               [controller performSearchWithString:searchText andTexts:[self textsToSearch]];
    });

Since I did this, my actual array is not picking up any of these objects (or it's immediately dumping them?)--it always logs "null". I have no idea why. Declaring the array as a __block object does not change it, nor does creating it as a property of the class. Here's my code for the method:

-(void)performSearchWithString:(NSString *)searchString andTexts:(NSSet *)texts
{

NSMutableArray *pendingResults = nil;
__block int resultsCount = 0;

NSError *error = nil;

for (id book in texts) {
    if ([book isEqual:kBook1]){

        NSURL *url = [[NSBundle mainBundle] URLForResource:@"neiJing" withExtension:@"txt"];



        NSString *text = [NSString stringWithContentsOfURL:url encoding:NSStringEncodingConversionAllowLossy error:&error];
        [text enumerateSubstringsInRange:NSMakeRange(0, [text length])
                                 options:NSStringEnumerationByWords
                              usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                                  //NSRange found = [substring rangeOfString:text];
                                  if ([searchString caseInsensitiveCompare:substring] == NSOrderedSame) {
                                      SearchResult *result = [[SearchResult alloc] init];
                                      result.title = @"neiJing";
                                      result.author = @"Author";
                                      //result.excerpt = substring; // Add logic to configure excerpt range
                                      result.range = NSMakeRange(substringRange.location-500, substringRange.length + 500);
                                      result.excerpt = [NSString stringWithFormat:@"...%@...",[text substringWithRange:result.range]];
                                      [pendingResults addObject:result];
                                      NSLog(@"%@", pendingResults);
                                      resultsCount++;
                                  }
                              }];
    }

if (error == nil) {
    [self.delegate SearchControllerDidReturnResults:[NSArray arrayWithArray:pendingResults]];
}

Greatly appreciate any help. I'm still really struggling to understand blocks, not having any background in straight "C", but thought I had a grasp of how variable scope was affected. Seems I'm still missing a critical concept.

Reid
  • 1,109
  • 1
  • 12
  • 27
  • 5
    Looks like `pendingResults` is never allocated? – Mike Mertsock Apr 01 '13 at 18:06
  • 2
    I'm pretty certain this is not the correct way to use `dispatch_async`. You should use your searchQueue thread to perform the search before your make your second call to `dispatch_async`, then reenter dispatch_get_main_queue() to update the UI with the results. – dalton_c Apr 01 '13 at 18:17

1 Answers1

4

It looks to me like you've encountered one of the peculiarities of Objective-C, namely that you can leave a variable set to nil and still send messages to it.

The program will just silently continue without any error messages, warnings or other signs that a variable might not be initialised.

Hence, when you set pendingResults = nil and then later call

[pendingResults addObject:result];

the effect will be that the message addObject: is sent to nil, with absolutely no result, not even a console message.

My guess is that if you change the declaration of pendingResults to:

NSMutableArray *pendingResults = [NSMutableArray array];

things will begin to happen. Maybe not the right things (it's a lot of code, many things can still go wrong) but you'll get something in your array.

While this (non-)handling of nil may seem like a "feature" that makes it more difficult to find bugs (and it is), it also means that Objective-C code in many cases is much cleaner than the "competition". For comparison, look at Java, which throws its dreaded NullPointerException left and right, leading to many people littering their code with tests for pointers being non-null.

So once you get the hang of this peculiar aspect of Objective-C it ends up being one of the first things you check in the debugger (and you do use the debugger, right?)

Monolo
  • 18,205
  • 17
  • 69
  • 103
  • Thank you so much! I'm embarrassed to have made such a rookie error--it was incredibly obvious once you pointed it out. The array was never initialized properly (my foggy 6AM head decided to set it to "nil" each time the method was called, thinking I needed to clear it). Many, many thanks! – Reid Apr 01 '13 at 19:03