关于iOS应用设计的一些最佳实践



时刻铭记着views的生命周期不要在init的方法中访问self.view



使用data source protocols(数据源协议)来明显地将dataview上区分开来
UIViewController

     使用已经存在的navigationitem对象

NSObject

     在头文件中仅暴露公有属性和方法
Debugging

     使用lldb来进行测试
     使用NSZombieEnabled来发现内存泄露

———————————————————————————————————————–

时刻铭记着views的生命周期

不断地提醒自己,在任何时候,你的view都可能被销毁

(1) 不要在init的方法中访问self.view

你永远不应该在你的controller的init方法中访问self.view。这么做总是会导致很多难于调试的bug,因为在收到一个内存警告之后init的逻辑将无法再次执行一遍。

考虑下面这个简单的例子:
- (id)initWithNibName:(NSString )nibNameOrNil bundle:(NSBundle )nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
self.view.backgroundColor = [UIColor underPageBackgroundColor];
}
return self;
}


想象一下这个controller是一个navigation stack(注:iOS中使用栈的结构来管理那些navigationController相关的视图对象)的根对象,与此同时一个内存警告发生了(收到内存警告如果没有正确处理,iOS系统会销毁一些视图以释放内存空间)。当我们返回到该控制器的时候,该视图将不再以underPageBackgroundColor作为其背景色。这会给调试带来麻烦,即便是对于那些有经验的iOS工程师也是如此。

(2) 使用data source protocols(数据源协议)来明显地将数据从视图上区分开来

当你正在设计一个视图来与数据集进行交互的时候,你总是应该通过一个data source protocol来获取数据,而不是通过暴露访问器(setter)的形式。视图不是数据的容器,所以不应该让它们承受这样的“罪责”。正确的做法是,视图应该被当做一个“失去存在感”的可扩展的组件,在任何时候都是。

作为一个一般性的通用法则,在视图上的任何超出静态展现的信息都应该通过data source或者delegate来获取。

UILabel就是一个不需要datasource的视图,它是个很好的示例。它所有的属性通常都被设置一次,并且通常在视图的生命周期内都不期待被改变。

而从另一方面来说,UITableView是一个需要data source来提取数据的好例子。让我们想象一下当我们使用UITableView的时候不是通过一个datasource而是仅仅通过提供setter的情况将会是什么样子。

如果真是这样设计的话,当开发者企图使用table view对象来存储他们的数据的时候,将会导致不可避免的滥用。当收到内存警告,table view 不可避免地被释放时,数据也将一同消失(因为它通过setter存储在tableview对象内部)。这意味着我们需要存储的数据能够在任何情况下北table view的多个实例在其生命周期内可以访问。

UIViewController

view controller用来完成model与view之间的绑定

(1)使用已存在的navigation item对象

每一个UIViewController的实例都有一个navigationItem属性,它应该被用来指定左/右导航按钮以及标题视图。你不应该创建一个UINavigationItem 对象,因为在你访问self.navigationItem的时候UIViewController的基类实现将自动帮你创建一个。简单地使用self.navigationItem来访问并为其设置属性:
// UIViewController will automatically create the navigationItem object.
self.navigationItem.rightBarButtonItem = doneButton;


NSObject

(1) 在头文件中仅暴露公有属性和方法

obj-c允许你在一个category interface(.m文件中)定义私有的属性。利用这个特性提供更好地访问限制吧。

这等价于通过@provate进行定义,并且能够带来一些额外的好处——对于代码的修改不需要重新编译并且不影响其他对象的内部结构。在一个大型项目中,这是大有裨益的。

示例:

ViewController.h

@interface ViewController : UIViewController
@property (nonatomic, readonly, assign) NSInteger objectId;
@end
ViewController.m
#import "ViewController.h"

@interface ViewController()
@property (nonatomic, readwrite, assign) NSInteger objectId;
// Notice that this property doesn’t need to be in the .h. Objective-C will create this
// property on the fly!
@property (nonatomic, readwrite, retain) UILabel* objectLabel;
@end

@implementation ViewController
@synthesize objectId;
@synthesize objectLabel;



@end


Debugging

(1)使用lldb来进行测试

lldb允许你检查类的属性,不需要在对象的实例上有明确地定义

(2)使用NSZombieEnabled来发现内存泄露

当NSZombieEnabled被使用,那些从内存中被释放的对象都将被保存为“zombies”。如果在未来的某个时间你企图再次访问这些已被释放的对象。这对于你去判断在什么地方发生内存泄露将会大有帮助。

(注:关于这两个debug的配置,请自行google)