UITableView和UITableViewController范圣刚,princetoad@gmail.com,使⽤用UITableView的例⼦子开始构建Homepwner•Homepwner:⼀一个库存管理的⼩小程序•第⼀一阶段⺫⽬目标:把前⾯面我们实现的BNRItem在UITableView中展⽰示出来•新建⼀一个EmptyApplication项⺫⽬目,命名为Homepwner,类前缀:HomepwnerUITableViewController•UITableView是⼀一个view对象,知道如何对⾃自⾝身进⾏行绘制;但并不处理程序逻辑或数据。•要使table能够正常⼯工作,下⾯面这些是必要的:•viewcontroller-控制UITableView在屏幕上的样⼦子•datasource-有多少⾏行数要显⽰示,以及这些⾏行显⽰示的数据。UITableView的dataSource可以是任何Objective-C对象,只要符合UITableViewDataSourceprotcol•delegate-UITableView⼀一般需要⼀一个delegate,可以通知其他对象涉及UITableView的⼀一些事件。这个delegate要遵循UITableViewDelegateprotocol•⼀一个UITableViewController类的实例就可以填补这三种⾓角⾊色:viewcontroller,datasource和delegateUITableViewController和UITableView•因为UITableViewController是UIViewController的⼀一个⼦子类,所以UITableViewController拥有⼀一个view•UITableViewController的view总是UITableView的⼀一个实例,并且UITableViewController处理UITableView的呈现和准备⼯工作•当UITableViewController⽣生成它的view时,UITableView的dataSource和delegate实例变量被⾃自动设成指向UITableViewControllerUITableViewController和UITableView关系⽰示意图⼦子类化UITableViewController•为Homepwner编写⼀一个UITableViewController的⼦子类•File-New-File,iOS-CocoaTouch-Objective-Cclass,命名:ItemsViewController,⽗父类是:UITableViewController•UITableViewController的designatedinitializer是initWithStyle:,使⽤用⼀一个常量来确定tableview的样式,有两个选项:•UITableViewStylePlain:每⾏行是⼀一个矩形•UITableViewStyleGrouped:顶部和底部⾏行具有圆⾓角•实现下列的initializers:init和initWithStyle•这就确保了不管发送什么初始化消息给ItemsViewController,所有的ItemsViewController的实例都使⽤用UITableViewStyleGrouped样式。-(id)init{//调⽤用超类的designatedinitializerself=[superinitWithStyle:UITableViewStyleGrouped];if(self){}returnself;}-(id)initWithStyle:(UITableViewStyle)style{return[selfinit];}•打开HomepwnerAppDelegate.m,在application:didFinishLaunchingWithOptions:中,创建⼀一个ItemsViewController的实例并把它设成window的rootViewController•确保在⽂文件顶部导⼊入ItemsViewController头⽂文件#importItemsViewController.h@implementationHomepwnerAppDelegate-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions{self.window=[[UIWindowalloc]initWithFrame:[[UIScreenmainScreen]bounds]];//Overridepointforcustomizationafterapplicationlaunch.ItemsViewController*ivc=[[ItemsViewControlleralloc]init];[[selfwindow]setRootViewController:ivc];self.window.backgroundColor=[UIColorwhiteColor];[self.windowmakeKeyAndVisible];returnYES;}空⽩白的UITableView•让每⾏行显⽰示⼀一个BNRItem的实例•把前⾯面实现的BNRItem的头⽂文件和实现⽂文件拖到Homepwner的projectnavigatorUITableView的DataSource•tableview会询问另⼀一个对象-它的dataSource-应该显⽰示什么•ItemsViewController就是datasource,因此需要⼀一种⽅方法来存储item数据•我们在前⾯面使⽤用⼀一个NSMutableArray来存储BNRItem实例,现在还是使⽤用NSMutableArray,但是会把它抽象成另⼀一个对象-BNRItemStoreHomepwner对象⽰示意图•BNRItemStore本⾝身包含⼀一个数组,它负责在这个数组上执⾏行操作,像排序,增加和删除BNRItem•也将负责从磁盘保存和加载BNRItem•下⾯面我们开始创建BNRItemStore对象BNRItemStore介绍创建BNRItemStore•创建⼀一个新的NSObject的⼦子类,命名:BNRItemStore•增加⼀一个类⽅方法:defaultStore:,实现单例模式+(BNRItemStore*)defaultStore{staticBNRItemStore*defaultStore=nil;if(!defaultStore){defaultStore=[[superallocWithZone:nil]init];}returndefaultStore;}#importFoundation/Foundation.h@interfaceBNRItemStore:NSObject//增加⼀一个类⽅方法,前缀++(BNRItemStore*)defaultStore;@end静态变量•静态变量不在栈内存放,当⽅方法返回时不会被销毁•静态变量会在程序被加载时只声明⼀一次,并且永远不会被销毁•静态变量(staticvariable)有点像本地变量,只能从声明它的⽅方法内访问它。因此没有其他的对象或⽅方法可以使⽤用由这个变量指向的BNRItemStore,除了通过defaultStore⽅方法静态变量指向的对象•defaultStore的初始值是nil•这个⽅方法第⼀一次被调⽤用的时候呢,⼀一个BNRItemStore的实例将被创建,并且defaultStore将会指向它•在后续的对这个⽅方法的调⽤用中,defaultStore仍将指向BNRItemStore的这个实例•这个变量具有对BNRItemStore的强引⽤用,并且由于这个变量永远不会被销毁,因此它指向的对象也永远不会被销毁确保BNRItemStore的单例状态•就要确保不会有另⼀一个BNRItemStore被分配•⼀一种⽅方法是重写BNRItemStore中的alloc,让它不创建⼀一个新的实例,⽽而是返回⼀一个已经存在的实例•这种⽅方法有⼀一个问题就是:因为历史的原因,alloc仅仅是对allocWithZone:的调⽤用,allocWithZone:然后调⽤用C函数NSAllocateObject,NSAllocateObject再执⾏行真正的内存分配•为了防⽌止跳过alloc:直接调⽤用allocWithZone:,我们要对allocWitheZone:进⾏行重写重写initWithZone://zone参数在Objective-C⾥里⾯面是没有⽤用的,忽略即可+(id)allocWithZone:(NSZone*)zone{return[selfdefaultStore];}•调⽤用NSObject的allocWithZone:实现,⽽而不是⾃自⾝身的allocWithZone:•defaultStore=[[superallocWithZone:nil]init];默认的allocationchainBNRItemStore和NSObjectallocation⽅方法保存BNRItem的数组•通过上⾯面的操作,我们就确保了不会创建多个BNRItemStore的实例,同时我们也确保了⼀一旦BNRItemStore的实例被创建,它就不会被销毁,因为⼀一个永远不会被销毁的静态变量⼀一直维护对它的ownership•下⾯面我们创建⼀一个⽤用于保存BNRItem的数组,和两个⽅方法@class•@class指⽰示符告诉编译器有⼀一个BNRItem类,并且在当前⽂文件不需要知道这个类的细节•这样就允许我们在没有导⼊入BNRItem.h的情况下也可以在createItem声明中使⽤用BNRItem符号•使⽤用@class可以加快编译速度,同时还可以避免⼀一些其他问题#importFoundation/Foundation.h@classBNRItem;@interfaceBNRItemStore:NSObject{NSMutableArray*allItems;}//增加⼀一个类⽅方法,前缀++(BNRItemStore*)defaultStore;-(NSArray*)allItems;-(BNRItem*)createItem;@end创建数组并实现相应⽅方法•在真正给BNRItem类发送消息或者对它进⾏行实例化的类中,必须导⼊入声明它的⽂文件,这样编译器才能知道它的细节•在BNRItemStore.m中,导⼊入BNRItem.h•在BNRItemStore.m中,重写init来⽣生成⼀一个NSMutableArray的实例并把它分配给实例变量•并且实现前⾯面声明的两个⽅方法代码#importBNRItemStore.h//导⼊入BNRItem头⽂文件#importBNRItem.h@implementationBNRItemStore-(id)init{self=[superinit];if(self){allItems=[[NSMutableArrayalloc]init];}returnself;}-(NSArray*)allItems{returnallItems;}-(BNRItem*)createItem{BNRItem*p=[BNRItemrandomItem];[allItemsaddObject:p];returnp;}实现数据源⽅方法•导⼊入头⽂文件•在designatedinitializer中增加5个随机条⺫⽬目到BNRItemStore•UITableViewDataSource•tableView:numberOfRowsInSection:•tableView:c