个人总结的代码编写规范及一些注意的地方:
<style type="text/css" id="wiz_todo_style_id" wiz_link_version="01.00.09">.wiz-todo, .wiz-todo-img {width: 16px; height: 16px; cursor: default; padding: 0 10px 0 2px; vertical-align: -10%;-webkit-user-select: none;} .wiz-todo-label { display: inline-block; padding-top: 7px; padding-bottom: 6px; line-height: 1.5;} .wiz-todo-label-checked { color: #666;} .wiz-todo-label-unchecked {text-decoration: initial;} .wiz-todo-completed-info {padding-left: 44px; display: inline-block; } .wiz-todo-avatar { width:20px; height: 20px; vertical-align: -20%; margin-right:10px; border-radius: 2px;} .wiz-todo-account, .wiz-todo-dt { color: #666; }</style>ios最佳实践
OSChina : http://git.oschina.net/DarkHorse1919/ios-good-practices-the-lastest-version
Github : https://github.com/KevinHM/ios-good-practices-the-lastest-version
、
2 、 MVVM 架构之 ReactiveCocoa 详解: iOS 的函数响应型编程
OSChina: http://git.oschina.net/DarkHorse1919/FunctionalReactiveProgrammingOniOS
Github : https://github.com/KevinHM/FunctionalReactiveProgrammingOniOS
3 、禅与 Objective-C 编程艺术 (主要贡献者及校验者)
Github : https://github.com/oa414/objc-zen-book-cn
1.代码编写顺序:先是life cycle(程序生命周期)
,然后是Delegate(代理)方法实现
,然后是event response(事件响应)
,然后才是getters and setters(viewOrAttribute的getters和setters)
。
示例:
#pragma mark - liftCycle
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}
#pragma mark -vc push pop
//view加载等代码
#pragma mark -searchData
//加载数据代码
#pragma mark - delegate
//代理委托代码
#pragma mark - eventRespone(eg: UITableViewDelegate & UITableViewDataSource Support)
//事件响应对应代码
#pragma mark - other
//一些共用代码
#pragma mark - getters and setter
//view加载等代码
#pragma mark - Layout
//layout代码
- (void)makeViewConstraints;
2.-viewDidLoad
中,做为逻辑的入口,代码会变少但是变清晰,代码如下:
[self.view addSubview:self.topView];
[self.view addSubview:self.bgView];
[self.view addSubview:self.tabView];
然后重写view的getter方法,包括View和frame这些都可以使用({...})语法使代码结构化层次化:
- (UIView *)topView{
if (!_topView) {
_topView = ({
UIImageView *imgBg = [UIImageView new];
imgBg.frame = CGRectMake(0, 0, 320, 49);
imgBg;
});
}
return _topView;
}
3.代理Delegate写到一块去,写好代理块注释:
如UITableViewDelegate的方法要写上:#pragma mark - UITableViewDelegate。这样可以方便阅读和查看代理 实现
4.event response(事件响应)专门开一个代码区域
所有button、gestureRecognizer的响应事件都放在这个区域里面,不要到处乱放。
5.关于private methods(私有方法),正常情况下ViewController里面不应该写
不是delegate方法的,不是event response方法的,不是life cycle方法的,就是private method了。对的,正常情况下ViewController里面一般是不会存在private methods的,这个private methods一般是用于日期换算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有一个函数也行。
ViewController基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放在ViewController里面就不要放。另外一点,这个private method的功能这时候只是你用得到,但是将来说不定别的地方也会用到,一开始就独立出来,有利于将来的代码复用。(引用之http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html)
6.AOP划分服务逻辑(对于像日志、异常系统、统计这种服务型代码,尽可能使用aop进行逻辑划分,避免过多耦合和干预ViewController的职责)
下面使用oc runtimer机制实现最常用的统计代码集成,以友盟统计为例,页面pv是衡量浏览量的主要指标,基本是必须集成的统计之一,页面pv由页面的进入和页面的结束组成,那么这就需要我们在viewController的生命周期中的viewWillAppear和viewWillDisappear里进行代码集成:
普通使用示例:
页面进入统计:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[MobClick beginLogPageView:@"PageOne"];
}
页面退出统计:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[MobClick endLogPageView:@"PageOne"];
}
这种类型的代码是要在所有的ViewController里都要写的,那么我们每个ViewController都写一遍?要是业务改变更换统计集成代码不需要友盟了,哪么不就每个页面都需要重新替换,哪得要猴年马月,聪明的你说不定想到,这还不简单,使用继承这不就解决这个问题了么,所有ViewController都继承统一的父ViewController,嗯,这个是有点道理,但是这就要求我们创建项目的时候需要创建好唯一的父类ViewController,并且所有的ViewController都要继承于它,对于项目已经进行到中期了,项目没有唯一的父类ViewController该怎么办呢?写一个?再把所有的ViewController翻出来继承于父ViewController,想想都要加班的节奏,讨厌死啦。而且项目中并不是所有ViewController类都继承于UIViewController,有可能继承UITableViewController,UIConllectionViewContrtoller,对于这类继承了其它扩展类型的ViewController,我们又该怎么办,fack重写一下吧,OMG...,想想都应该叫老板,给我来碗“泪流满面”,说回重点,对于以上所说的问题,我们可以使用oc 的runtimer机制完美解决。
oc runtimer可以参考:
http://www.cocoachina.com/ios/20150120/10958.html
http://my.oschina.net/panyong/blog/298631
简单多说一下,什么是runtimer机制,说白了就是运行时机制,它的作用是可以动态注入代码,动态创建类,创建属性等等。专业人士称它为黑魔法。下面就以runtimer机制修改上面的代码:
1.创建一个UIViewController的category,并引入
#import <objc/runtime.h>
2.实现+(void)load方法,并在load方法实现方法注入,代码如下:
+ (void)load {
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
Class class = [self class];
swizzleMethod(class, @selector(viewDidLoad), @selector(aop_viewDidLoad));
swizzleMethod(class, @selector(viewDidAppear:), @selector(aop_viewDidAppear:));
swizzleMethod(class, @selector(viewWillAppear:), @selector(aop_viewWillAppear:));
swizzleMethod(class, @selector(viewWillDisappear:),@selector(aop_viewWillDisappear:));
});
}
void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class,originalSelector,method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));if (didAddMethod) {
class_replaceMethod(class,swizzledSelector,method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (void)aop_viewDidAppear:(BOOL)animated {
[self aop_viewDidAppear:animated];
}
-(void)aop_viewWillAppear:(BOOL)animated {
[self aop_viewWillAppear:animated];
[MobClick beginLogPageView:NSStringFromClass([self class])];
}
-(void)aop_viewWillDisappear:(BOOL)animated {
[self aop_viewWillDisappear:animated];
[MobClick endLogPageView:NSStringFromClass([self class])];
}
- (void)aop_viewDidLoad {
[self aop_viewDidLoad];
if ([self isKindOfClass:[UINavigationController class]]) {
}
}
上面代码出现了一个陌生的单词swizzleMethod,swizzleMethod是利用 Runtime 特性把一个方法的实现与另一个方法的实现进行替换。
swizzleMethod是一个非常好用的特性,代码库,框架的封装常常可以看到它哦。
第三方对swizzleMethod的封装:
Aspects:https://github.com/steipete/Aspects
http://www.ios122.com/2015/08/aspects/
AOP相应注入Deom:https://github.com/okcomp/AspectsDemo
swizzleMethod参考:
http://blog.csdn.net/yiyaaixuexi/article/details/9374411
http://blog.csdn.net/mangosnow/article/details/34908365
SEL(@selector)参考:
http://blog.csdn.net/fengsh998/article/details/8612969
7.Block代替delegate,尽量使用block,对于有大量的delegate方法才考虑使用protocol实现.
1.Block语法总结及示例如下:
//1.普通代码块方式block
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {
// block code
};
使用未例:
int (^abc)(int a) = ^int(int a){
return a+1;
};
int aa = abc(2);
NSLog(@"%d",aa);
//2.属性方式block
@property (nonatomic, copy) returnType (^blockName)(parameterTypes);
使用示例:
1.定义属性
@property (nonatomic,copy) int (^testblock)(NSString *);
2.设置使用属性
[self setTestblock:^int(NSString *a) {
return 0;
}];
//3.方法参数block
- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName {
// block code
};
使用示例1:
1.无参数型定义及实现:
- (void)testBlockFun:(void(^)())completion{
NSLog(@"执行");
if (completion) {
completion();
}
}
2.无参数型block调用:
[self testBlockFun:^{
NSLog(@"回调结束");
}];
使用示例2:
1.带参数型定义及实现:
- (void)testBlockFun:(int (^)(int a,int b))complate{
if (complate) {
int c = complate(3,5);
NSLog(@"c:%d",c);
}
}
2.带参数型block调用:
[self testBlockFun:^int(int a, int b) {
return a+b;
}];
// 4.作为参数
[someObject someMethodThatTakesABlock: ^returnType (parameters) {
// block code
}];
使用示例:
1.定义及实现
- (void) testBlockFun:(void (^)(NSString *))complate{
if (complate) {
complate(@"success");
}
}
2.调用
[self testBlockFun:^(NSString *str) {
NSLog(@"str:%@",str);
}];
// 5.使用 typedef 定义
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^(parameters) {
};
使用示例:
typedef void (^blockTestName)(NSString *);
调用:
[self setName:^(NSString *a){
}];
2.Block修改值:使用__block可以在block内部修改外部变量的值。
__block int someIncrementer = 0;
[someObject someMethodThatTakesABlock:^{
someIncrementer++;
}];
3.Block循环引用,block会持有对象,block的对象也有block,会造成block的循环引用,解决方法:
__weak typeof(self) weakSelf = self;//@weakify(self);
[self someMethodThatTakesABlock:^{
[weakSelf action];
}];
8.错误处理注意:
如下处理会有问题:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
}
在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。
应该如下:
NSError *error;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
Block weakSelf宏:
#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;
9.方法命名注意:
方法使用小驼峰法命名, 一个规范的方法读起来应该像一句完整的话,读过之后便知函数
的作用。执行性的方法应该以动词开头,小写字母开头,返回性的方法应该以返回的内容
开头,但之前不要加get。
示例:
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
(instancetype)arrayWithArray:(NSArray *)array;
如果有参数,函数名应该作为第一个参数的提示信息,若有多个参数,在参数前也应该有
提示信息(一般不必加and)
一些经典的操作应该使用约定的动词,如initWith,insert,remove,replace,add等等
设置属性时能用.符号的尽量使用.符号,这样可以让表达式更加的清晰明了,容易区分出是属性调用还是方法调用
10.枚举、通知统一管理
11.多个target管理项目版本(开发、正式)
12.单例:
+ (AccountManager *)sharedManager
{
static AccountManager *instance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
instance = [[self alloc] init];
});
return instance;
}
13.打印运行方法所在的Vc及方法名
NSLog(@"%s",__func__);
14.参考额外文章 :
http://www.jianshu.com/p/3beb21d5def2
http://www.jianshu.com/p/06bab2058c53
请求缓存:
http://www.jianshu.com/p/fb5aaeac06ef
Parse源码浅析系列(一)---Parse的底层多线程处理思路:GCD高级用法
###如有更好的建议请联系qq:1213423761