iOS代码组织和架构总结
好长时间没写博客了,今天写一篇把从别人那里学到的和自己平时用到的架构作个总结,最后归结为一个demo。
- 代码文件结构
- viewController代码规范
- 跳转管理
- 第三方库管理
- 数据和图片存储
- 网络请求工具
代码文件结构
- 文件组织结构与平时流行的差不多,体现了一种MVC的思想。假设项目有三个功能模块,首页,更多和登陆,以下是基本的文件结构。
- Me
- More
- Base
- common
- Home
- Libs
- More
- Utils
Me和More为功能模块,里面有Controllers,Views和Models文件夹,这三个文件夹顾名思义就是每个页面的ViewController,视图组件和数据模型。
Base文件夹下是BaseNavigationController,BaseTabbarController和BaseUIViewController。这个文件夹定义得基本的导航tab和共同的controller基类。
Common文件夹就比较复杂一些。首先全局使用的宏需要放到这里我在这里建立了一个专门放置宏定义的文件夹Macros。其中的文件如下:LSLConstantMacros定义了一些常量,LSLUtilsMacros常用工具宏,LSLUrlMacros跳转路径宏,LSLUserDefaultsMacros在userDefaults里定义的key。
Libs放置引用的经过修改的第三方类库或者是自己写的类库
Utils放置共同使用的工具类。这个也可以在每个模块里建。其中有网络请求封装好的工具类。
ViewController代码规范
controller的代码大体分为如下几部分:生命周期相关(viewDidLoad等),代理方法,点击方法,get和set方法。借鉴其他大神的规范,这么组织代码:每一部分都用promark标记,第一部分为lifeCycle,将loadView,viewDidLoad等与生命周期有关的代码放在这里。大部分的view采用懒加载的方式,放在最后。设置frame的代码和创建view的代码一定要分开,以方便后边同事的阅读和修改中间为工具类方法,点击方法区等。如果不是成员变量的视图可以设置tag取得,为了方便阅读,建议使用宏定义或设置成枚举。示例代码如下:
@interface LSLHomeViewController ()
@property (nonatomic,strong) UIButton *gotoDetailPageButton;
@end
@implementation LSLHomeViewController
#pragma mark - lifeCycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureUI];
[self configureSubViewsFrame];
}
- (void)configureUI
{
[self.view addSubview:self.gotoDetailPageButton];
}
- (void)configureSubViewsFrame
{
self.gotoDetailPageButton.frame = CGRectMake(20, 100, CGRectGetWidth(self.view.bounds) - 40, 30);
}
#pragma mark - click
- (void)gotoDetailPage
{
[LSLForwardService openHomeDetailPage:^(LSLHomeDetailController *controller) {
}];
}
#pragma mark - get&&set
- (UIButton *)gotoDetailPageButton
{
if (_gotoDetailPageButton == nil) {
_gotoDetailPageButton = [UIButton buttonWithType:UIButtonTypeCustom];
_gotoDetailPageButton.backgroundColor = [UIColor lightGrayColor];
_gotoDetailPageButton.layer.cornerRadius = 3.5;
_gotoDetailPageButton.layer.masksToBounds = YES;
[_gotoDetailPageButton setTitle:@"前往detail界面" forState:UIControlStateNormal];
[_gotoDetailPageButton addTarget:self action:@selector(gotoDetailPage) forControlEvents:UIControlEventTouchUpInside];
}
return _gotoDetailPageButton;
}
跳转管理
跳转的话集中到一个跳转的service中,在那里控制所有的跳转。实现机制:在baseViewController里获得当前的NavigationController,作为push用的导航控制器,present的跳转则设置状态机进行判断。
- (void)viewWillAppear:(BOOL)animated
{
if (self.navigationController) {
[LSLForwardService sharedInstance].navigationController = self.navigationController;
}
}
跳转用代码:
+ (instancetype )sharedInstance {
static LSLForwardService *service;
if (service == nil) {
service = [[self alloc] init];
}
return service;
}
- (void)forwarToViewController:(UIViewController *)controller type:(LSLForwardType)type
{
if (type == LSLForwardTypePush) {
if (self.navigationController != nil) {
[_navigationController pushViewController:controller animated:YES];
} else {
return;
}
} else if (type == LSLForwardTypePresent){
[_navigationController presentViewController:controller animated:YES completion:NULL];
}
}
+ (void)openLoginPage:(void (^)(LSLLoginViewController *))configureBlock
{
LSLLoginViewController *pvc = [[LSLLoginViewController alloc] init];
LSLBaseNavigationController *nav = [[LSLBaseNavigationController alloc] initWithRootViewController:pvc];
[[LSLForwardService sharedInstance] forwarToViewController:nav type:LSLForwardTypePresent];
pvc.isPresentViewController = YES;
}
第三方库的管理
- 对于不需修改的第三方的代码可以直接使用cocoaPod管理,如果需要修改才能使用的放在libs文件夹中。cocoaPod的使用参考如下教程[用CocoaPods做iOS程序的依赖管理][http://www.devtang.com/blog/2014/05/25/use-cocoapod-to-manage-ios-lib-dependency/]
数据和图片存储
首先是项目所需的图片:以前是自己创建文件夹,后来苹果推出了images.xcassets,极大的方便了我们对图片的管理。在xcassets里管理图片可以新建文件夹对图片进行分类。如果要将图片转移到其他项目中直接右键打开响应的folder拷贝过去即可。图片的存储信息都放在了content.json中,是非常容易阅读的。对于单个图片我们可以在里面设置拉伸,是否渲染,适配的机型等。
然后是数据的存储。对于密码用户名等可以存储在钥匙串中(注意不要频繁的读写,容易崩溃),推荐个第三方的[https://github.com/soffes/sskeychain] github上还有好多,而且苹果官方有demo,这里就不列举了。另外,有的app可能要使用唯一识别号。苹果官方提供的[[[UIDevice currentDevice] identifierForVendor] UUIDString],如果程序被卸载重装或者系统升级该识别号会被重置,建议放在钥匙串中,避免重置。小的状态机等数据可以放在NSUserDefaults中,这里不适合放放大量的数据。最后是沙盒。沙盒的存储机制这里就不赘述了。特别要提的是document文件夹存放一些重要的数据如数据库,进度等数据。Cache文件夹存放网络缓存数据,每隔一段时间清理一次,如果有内容警告自己也清理下。对于内存缓存使用NSCache比使用NSDictionary更好,NSCache在系统内容紧张时会自动清理,有兴趣的可以去研究下SDWebimage,里面的内存处理非常的优雅。如果自己写工具类存储信息还可以使用NSMapTable等可以进行弱引用的集合[http://www.cocoachina.com/industry/20140122/7735.html]。
网络请求工具
**网络请求工具的话,很老的项目用过ASI,最近用的都是AFNetworking。考虑到网络请求的多样性,最好对网络请求进行进一步封装。这里推荐一个吧:[https://github.com/mogujie/MGJRequestManager],感觉非常好用,可以进行大量的个性化配置。github上好多类似项目,根据自己的项目自己封装也是不错的。
欢迎大家批评和建议!