views:

180

answers:

0

Hi there,

I'm having very strange issue with NSMutableArray class (as it seems at least). I have UITableViewController with custom data source class, which loads data from underlaying model class. Now I need to incorporate AdMob ads to the table. There can be one or more ad positions, typically at the top of the table and at the 3rd row of the table.

So what I do is as following:

  1. When model class finished loading data from database, it informs datasource about it via delegate method tableViewDidLoadModel

  2. Data source reloads table data [tableView reloadData] and starts loading the ads by calling helper method [self loadAds] which looks like this:

    if(_adPositions.count > 0) {
    
    
    if(_adsQueue == nil) {
        self.adsQueue = [[NSMutableDictionary alloc] initWithCapacity: _adPositions.count];
    }
    
    
    [_adsQueue removeAllObjects];
    
    
    for(NSIndexPath *position in _adPositions) {
        if(position.row <= _items.count) {
            /*
             cellWithAd is helper method which creates and returns the cell like this:
    
    
    
         WLAdCellView *cell = [[[WLAdCellView alloc] init] autorelease];
         [cell.contentView addSubview: [AdMobView requestAdWithDelegate: self]];
    
    
         return cell;
         */
        UITableViewCell *cell = [self cellWithAd];
        [_adsQueue setObject: cell forKey: position];
        }
    }
    }
    

So, it creates cell instance and puts it into the _adsQueue dictionary and AdMobView starts loading the ad strip in background. When ad is loaded, it calls delegate method - (void) didReceiveAd: (AdMobView *) adView which is implemented like this:

- (void) didReceiveAd: (AdMobView *) adView {

UITableViewCell *cell = nil;
NSIndexPath *key = nil;
NSArray *indexPaths = nil;

for(key in [_adsQueue allKeys]) {

    cell = [_adsQueue objectForKey: key];

    if([[cell.contentView.subviews objectAtIndex: 0] isEqual: adView]) {

        /// Move cell from queue to items array
        [((NSMutableArray *)_items) insertObject: cell atIndex: key.row];
        indexPaths = [NSArray arrayWithObject: key];

        [_adsQueue removeObjectForKey: key];

        break;
    }
}

if(indexPaths != nil) {

    UITableViewController *delegate = (UITableViewController *)self.delegate;

    [delegate.tableView beginUpdates];

    [delegate.tableView insertRowsAtIndexPaths: indexPaths withRowAnimation: UITableViewRowAnimationTop];

    [delegate.tableView endUpdates];
}
}

The logic here is quite simple: Find the cell object in _adsQueue dictionary, insert it into _items array (which actually holds the data for tableview) at appropriate place and remove it from _adsQueue. Note that when it's inserted into the array, it should get retain message according to docs, so it should not be freed until it's removed! Then update tableView by calling insertRowsAtIndexPaths: method in begin/endUpdate block. After that, tableView:cellForRowAtIndexPath: is called. Code concerning ads there looks like this:

if([self isAdPosition: indexPath]) {
    if([[_items objectAtIndex: indexPath.row] isKindOfClass: [WLAdCellView class]]) {
        return (UITableViewCell *)[_items objectAtIndex: indexPath.row];
    }
}

 /// Else create regular cell

The issue is that when ads are loaded (they are indeed!), then when I scroll down and back to the top, the second ad disappears and empty cell is returned instead! Interesting is that this behavior is somewhat random - sometime it works well, but occassionally (but often enough) it behave like I've described above.

Situation than is that object in _items array at required position is not recognized as WLAdCellView instance and - when debugged - it's really missing in it. Of course, without any manipulation with _items array in meantime. First ad instance is present always however.

I was debugging and examining it with instruments for hours but I can't figure out what's wrong with it and I'm getting mad out of it, so any help is highly appreciated.

thanks for any hints and advice

Matthes