arnaudambro
arnaudambro

Reputation: 2653

Why the numberOfRowsInSections: method is invoked 5 times ? And the numberOfSections 6 times?

I am posting again this question, which has quite been asked already in this topic: When is tableView:numberOfRowsInSection: called in UITableView?

I am following the Big Nerd Ranch lesson, and it appears that this method is invoked 5 times in my program, and I don't understand why... It is invoked far after the viewDidLoad, and even after the initialization. But it is invoked before the tableView:cellForRowAtIndexPath: method is invoked. I put breakpoint in my code everywhere, and NSLogs everywhere too (I deleted some of them in my code to make it clearer), but still I have no idea what's going on between each time it's invoked.

Here is my code:

BNRAppDelegate.h

#import <UIKit/UIKit.h>

@interface BNRAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

BNRAppDelegate.m

#import "BNRAppDelegate.h"
#import "BNRItemsViewController.h"

@interface BNRAppDelegate ()

@end

@implementation BNRAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.

//Create a BNRItemsViewController
BNRItemsViewController *itemsViewController = [[BNRItemsViewController alloc] init];

//Place BNRItemsViewController's table view in the window hierarchy
self.window.rootViewController = itemsViewController;

self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
//etc...

@end

BNRItem.h

#import <Foundation/Foundation.h>

@interface BNRItem : NSObject
{
NSString *_itemName;
NSString *_serialNumber;
int _valueInDollars;
NSDate *_dateCreated;
}


@property NSString *itemName;
@property NSString *serialNumber;
@property int valueInDollars;
@property NSDate *dateCreated;

+(instancetype)randomItem;

//Designated initializer for BNRItem
-(instancetype)initWithItemName:(NSString *)name
             valueInDollars:(int)value
               serialNumber:(NSString *)sNumber;

-(instancetype)initWithItemName:(NSString *)name;


@end

BNRItem.m

#import "BNRItem.h"

@implementation BNRItem

+(instancetype)randomItem
{
//Create an immutable array of three adjectives
NSArray *randomAdjectiveList = @[@"Fluffy", @"Rusty", @"Shiny"];

//Create an immutable array of three nouns
NSArray *randomNounList = @[@"Bear", @"Spork", @"Mac"];

//Get the index of a random adjective/noun from the lists
//Note: the % operator, called the modulo operator, gives you the remainder. So adjectiveIndex is a random number from 0 to 2 inclusive.
NSInteger adjectiveIndex = arc4random() % [randomAdjectiveList count];
NSInteger nounIndex = arc4random() % [randomNounList count];

//Note the NSInteger is not an object but a type definition for "long"

NSString *randomName = [NSString stringWithFormat:@"%@ %@",
                        [randomAdjectiveList objectAtIndex:adjectiveIndex],
                        [randomNounList objectAtIndex:nounIndex]];

int randomValue = arc4random() % 100;

NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c",
                                '0' + arc4random() % 10,
                                'A' + arc4random() % 26,
                                '0' + arc4random() % 10,
                                'A' + arc4random() % 26,
                                '0' + arc4random() % 10];

BNRItem *newItem = [[self alloc] initWithItemName:randomName
                                   valueInDollars:randomValue
                                     serialNumber:randomSerialNumber];
return newItem;
}


-(instancetype)initWithItemName:(NSString *)name
             valueInDollars:(int)value
               serialNumber:(NSString *)sNumber
{
//Call the superclass's designated initializer
self = [super init];

//Did the superclass's designated initializer succeed?
if (self) {
    //Give the instance variables initial values
    self.itemName = name;
    self.serialNumber = sNumber;
    self.valueInDollars = value;
    //Set _dateCreated to the current date and time
    self.dateCreated = [[NSDate alloc] init];
}

//Return the address of the newly initialized object
return self;
}

-(instancetype)initWithItemName:(NSString *)name
{
return [self initWithItemName:name
               valueInDollars:0
                 serialNumber:@""];
}

-(instancetype)init
{
    return [self initWithItemName:@"Item"];
}

-(NSString *)description
{
NSString *descritptionString = [[NSString alloc] initWithFormat:@"%@ (%@): worth $%d, recorded on %@",
                                self.itemName,
                                self.serialNumber,
                                self.valueInDollars,
                                self.dateCreated];
return descritptionString;
}
@end

BNRItemStore.h

#import <Foundation/Foundation.h>

@class BNRItem;

@interface BNRItemStore : NSObject

@property (nonatomic, readonly) NSArray *allItems;

+(instancetype)sharedStore;
-(BNRItem *)createItem;

@end

BNRItemStore.m

#import "BNRItemStore.h"
#import "BNRItem.h"

@interface BNRItemStore ()

@property (nonatomic) NSMutableArray *privateItems;

@end

@implementation BNRItemStore

+(instancetype)sharedStore
{
    static BNRItemStore *sharedStore = nil;

//Do I need to create a sharedStore ?
if (!sharedStore) {
    sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}

//If a programmer calls [[BNRItemsStore alloc] init], let him know the error of his ways
-(instancetype)init
{
@throw [NSException exceptionWithName:@"Singleton"
                               reason:@"Use +[BNRItemStore sharedStore]"
                             userInfo:nil];
return nil;
}

//Here is the real (secret) intializer
-(instancetype)initPrivate
{
self = [super init];
if (self) {
    _privateItems = [[NSMutableArray alloc] init];
}
return self;
}

-(NSArray *)allItems
{
return self.privateItems;
}

-(BNRItem *)createItem
{
BNRItem *item = [BNRItem randomItem];

[self.privateItems addObject:item];

return item;
}

@end

BNRItemsViewController.h

#import <UIKit/UIKit.h>
#import "BNRItemsViewController.h"
#import "BNRItemStore.h"
#import "BNRItem.h"

@interface BNRItemsViewController : UITableViewController

@property (nonatomic) BNRItemStore *itemStore;

@end

BNRItemsViewController.m

#import "BNRItemsViewController.h"
#import "BNRItemStore.h"
#import "BNRItem.h"

@implementation BNRItemsViewController

-(BNRItemStore *)itemStore
{
BNRItemStore *itemStore = [BNRItemStore sharedStore];
return itemStore;
}

//Designated Initializer is initWithStyle  Changing to init
-(instancetype)init
{
//ALWAYS call the superclass's designated initializer
self = [super initWithStyle:UITableViewStylePlain];
if (self) {
    for (int i = 0; i < 8; i++) {
        BNRItem *item = [self.itemStore createItem];
        NSLog(@"The %@, valued %d has been created. It is at index %lud", item.itemName, item.valueInDollars, (unsigned long)[[[BNRItemStore sharedStore] allItems] indexOfObject:item]);

    }
}
return self;

}



-(instancetype)initWithStyle:(UITableViewStyle)style
{
return [self init];
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(@"This tableview has %ld section and %lu rows", (long)section, (unsigned long)[[self.itemStore allItems] count]);
return [[self.itemStore allItems] count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView
    cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
//Get a new or recycled cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"  forIndexPath:indexPath];

//Set the text on the cell with the description of the item that is at the nth index of items, where n = row this cell will appear in on the tableview
NSArray *items = [self.itemStore allItems];
BNRItem *item = items[indexPath.row];
NSLog(@"%@ is in indexPath %ld - %ld", item.itemName, (long)[indexPath section], (long)[indexPath row]);


cell.textLabel.text = [item description];

return cell;
}

-(void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"ViewDidLoad1");
[self.tableView registerClass:[UITableViewCell class]
       forCellReuseIdentifier:@"UITableViewCell"];
NSLog(@"ViewDidLoad2");
}

@end

I also have another code where I have 2 sections, and so I invoked the method numberOfSections:, and this one is called 6 times ! Two times consecutively first, and the alternatively with numberOfRowsInSection: What the hell ?

Upvotes: 2

Views: 151

Answers (1)

Gerd Castan
Gerd Castan

Reputation: 6849

The super class (the UITableView / Controller) does not cache those values, which is a good thing: You don't tell the superclass, when the number has changed, it has to ask. This is consistent with the whitebox approach that Apple chose.

So whenever an algorithm in Apples superclass implementation needs to know these values, it has to call. And when Apple changes some algorithm, the number of calls may change. I would not overthink this. There is nothing wrong.

The only conclusion: implement those methods as cheaply as possible.

Upvotes: 2

Related Questions