让代码更帅一点

博主的私人博客

写代码最重要的是实现功能,但是除了实现功能之外,我们还应该想办法,让代码变得更规范,更漂亮

最近在读《禅与Objective-C编程艺术》《Effective Objective C 2.0:编写高质量iOS与OS X代码的52个有效方法》,这两本都讲解了代码规范方面的东西,结合自己平时的代码习惯,发现有很多地方自己做的还是不够好,代码写得不够帅,所以总结一下,让以后的代码更帅一点

条件语句

条件语句一定要使用括号,如果不使用括号,if后面的那行代码删除,之后的代码会成为if语句里面的代码

推荐:

if (!error) {
    return success;
}

括号

大括号的使用最好跟Apple保持一致,大括号在同一行开始,在新的一行结束,

如:

if (user.isHappy) {
    //Do something
}
else {
    //Do something else
}

nil和BOOL判断

最好直接使用!判断nil,或者BOOL值

推荐:

if (![someObject boolValue]) { ...
if (!someObject) { ...

避免嵌套if

不要嵌套 if 语句。使用多个 return 可以避免增加循环的复杂度,并提高代码的可读性。

推荐:

- (void)someMethod {
  if (![someOther boolValue]) {
      return;
  }

  //Do something important
}

不推荐:

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}

多用字面量语法

何为字面量语法

NSString *string = @"string";

NSNumber *intNumber = @88;
NSNumber *boolNumber = @YES;
NSNumber *floatNumber = @3.14;
int var = 3;
NSNumber *varNumber = @(var);

NSArray *list = @[@"itme1",@"item2",@"item3"];
NSLog(@"%@,%@,%@",list[0],list[1],list[2]);
NSDictionary *map = @{@"key1":@"value1",@"key2":@"value2"};
NSLog(@"%@,%@",map[@"key1"],map[@"key2"]);

这就是字面量语法,用非常简单直观的方法创建或者获取常用的对象(NSString,NSNumber,NSArray,NSDictionary),使用字面量语法简单方便直观

复杂的表达式

当有一个复杂的if语句时,可以把判断条件提取出来作为BOOL变量

BOOL nameContainsSwift  = [sessionName containsString:@"Swift"];
BOOL isCurrentYear      = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession     = nameContainsSwift && isCurrentYear;

if (isSwiftSession) {
    // Do something very cool
}

用常量代替宏

推荐:

static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const NSTimeInterval animationDuration = 3.0;

不推荐:

#define ZOCCacheControllerDidClearCacheNotification @"ZOCCacheControllerDidClearCacheNotification"
#define animationDuration 3.0

原因:

1. 使用宏的过程中无法直接知道常量类型(是int呢?还是Stiring呢?),除非查看宏定义的地方

2. 宏可以更改,,如果2个地方使用同样的名字定义了2个不一样的宏,你使用的时候可能会使用错误,造成难以发现的bug。使用const定义的常量如果更改编译器就会报错

static

在常量的使用过程中分两种情况

1.常量需要对外暴露。不使用static,并在.h中用extern暴露出去

// .h
extern NSString *const ZOCCacheControllerDidClearCacheNotification;
// .m
NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";

2.常量只在本类中使用,不需要对外暴露,使用static.

static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";

static定义的变量仅在当前的.m文件中可见,如果常量只在本类中使用,用static会避免影响到其他类,如果需要全局可见,就不能使用static

命名冲突

1)类名,全局变量和C函数(C函数类似于全局变量)命名时,使用三个字母作为前缀,避免命名跟第三方API或者苹果API名称冲突

项目里经常看到不使用前缀或者使用两个字母作为前缀的命名方式。

不使用前缀可能发生冲突不言而喻,那为什么不使用两个字母作为前缀呢?

因为Apple宣称其保留使用所有”两字母前缀”的权利,使用两字母作为前缀有跟未来的苹果API冲突的风险,所以保险起见,尽量使用三个字母作为iOS的命名前缀

2)类内部使用的私有方法,可以加个前缀,但不要使用单个下划线_作为前缀,因为这也是Apple预留的。可以使用双下划线__或者p_作为前缀

3)如果需要为无源码的类(包括第三方类和系统类)添加category,方法名称前最好带前缀,避免冲突

Designated Initializer

子类重新定义designated initializer时应遵循以下步骤:

  1. 定义designated initializer,并调用父类的designated initializer
  2. 重载父类的designated initializer,并调用新定义的designated initializer
  3. 为新的designated initializer写文档

在.h中,使用__attribute__((objc_designated_initializer))标明哪个是designated initializer。

如果因为某些原因,父类的designated initializer被弃用,比如designated initializer必须要有某个参数,父类的designated initializer无法传这个参数。用__attribute__((unavailable("Invoke the designated initializer")))表示弃用

- (instancetype)initWithName:(NSString *)name __attribute__((objc_designated_initializer));
- (instancetype)init __attribute__((unavailable("Invoke -initWithName:")));

类的封装

在封装一个类的时候,下面几点需要注意:

  1. 外部不需要了解的属性和方法,尽量定义在.m中
  2. 不要轻易暴露属性的setter方法,多使用readonly
  3. 尽量使用不可变对象
  4. 不要暴露可变的collection,应该提供相应的方法替代

protocol

1)一般情况下protocol分为2类,delegate和dataSource

  1. delegate:委托别的类去干活的,需要放在delegate里面,一般没有返回值
  2. dataSource:去别的类获取数据的,放在dataSource里面,主要用于获取数据

2)delegate属性在ARC下,一定要用weak取代assign,避免隐患

3)对于option的protocol。在调用之前一定要用-respondsToSelector:判断这个方法是否实现了

属性

规范

从代码规范和漂亮的角度来说,属性最好这样写,这是apple的写法

推荐:

@property(nonatomic, readonly, copy) NSString *nibName;

不推荐:

@property(nonatomic, readonly, copy) NSString* nibName;
@property(nonatomic, readonly, copy)NSString *nibName;
@property (nonatomic, readonly, copy) NSString *nibName;
...
setter&getter

属性除了init和dealloc外,建议使用setter和getter方法。在init和dealloc中,建议直接使用ivar

使用setter和getter方法的好处:

  1. 遵守内存管理语义(strong,copy…)
  2. kvo通知会自动被执行
  3. 方便debug,打断点
  4. 方便重写getter或者setter

为什么不要在init和dealloc中使用setter和getter?

如果init中使用了setter,在子类的属性初始化之前,调用[super init]的时候就会调用setter方法。这时候如果重写了setter,并在setter中做了特殊的操作,这时候就可能会引起一些非常难发现的bug。所以为了避免隐患,在init或者dealloc中直接使用ivar

可变对象赋值

对可以用可变对象赋值的属性(如:NSString,NSArray,NSDictionary),属性的内存管理类型必须为copy。这是为了防止可变对象(NSMultableArray)给不可变对象(NSArray)赋值。导致不可变对象(NSArray)指针指向可变对象(NSMultableArray),在使用过程中可能会改变它的值,出现bug

点符号

当使用属性的时候尽量使用.符号,方法调用使用[]

例:

 view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

Block

推荐这种block使用方法

__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomethingWithData:data];
        [strongSelf doSomethingWithData:data];
    }
}];

在block外面用weak持有self,避免循环引用

在block里面用strong持有self,避免多线程中block执行到一半,self被释放为nil,而出现的隐患

其他的block问题,可以看以前写的这篇文章

可变集合

永远不要枚举可变集合

推荐:

NSArray *staticArray = [multableArray copy];
for (id item in staticArray) {
    ....
}

不推荐:

for (id item in multableArray) {
    ....
}

可变集合在枚举时如果发生改变,会引起crash。

永远不要枚举可变集合,不管你有多么确定这个集合不会改变。因为代码也许以后会被修改,也许有别的线程会改变这个集合,太多意外可能会发生,唯一能保证不会有意外的就是永远不要枚举可变集合

不要将可变集合暴露成公共属性

推荐:

// .h
@property(nonatomic, readonly) NSArray *staticArray;

// .m
@property(nonatomic, strong) NSMutableArray *mutableArray;

- (NSArray *)staticArray
{
    return [self.mutableArray copy];
}

不推荐①:

// .h
@property(nonatomic, readonly)NSMutableArray *mutableArray;

不推荐②:

// .h
@property(nonatomic, readonly) NSArray *staticArray;

// .m
@property(nonatomic, strong) NSMutableArray *mutableArray;

- (NSArray *)staticArray
{
    return self.mutableArray;
}

你永远不知道外面会怎么用这个mutableArray,假设外面正在枚举这个mutableArray,在其他线程mutableArray被改变了。嘭,Crash了,还不是毕现,又是加班的节奏了…

如果在外部需要改变这个Array里面的item,添加改变item的方法:

// .h
@property(nonatomic, readonly) NSArray *staticArray;

- (void)addItem:(id)item;
- (void)removeItem:(id)item;

//.m
@property(nonatomic, strong) NSMutableArray *mutableArray;

- (void)addItem:(id)item
{
    [self.mutableArray addObject:item];
}

- (void)removeItem:(id)item
{
    [self.mutableArray removeObject:item];
}

NSNotification

remove

notification一定要remove!

notification一定要remove!!

notification一定要remove!!!

重要的东西一定要说三遍,Notification不remove很容易crash,包括KVO,也一定要记得remove,否则会有crash在前方等着你…

多次注册

notification如果多次注册会导致一次post,方法多次被调用,所以注意注册通知的时候一定要看清楚,是否通知只注册了一次,建议在init中注册通知,dealloc中remove通知

Reference

《禅与Objective-C编程艺术》

Effective Objective C 2.0:编写高质量iOS与OS X代码的52个有效方法

How Not to Crash

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-03 22:39:11

让代码更帅一点的相关文章

分享我对代码命名的一点思考和理解

一个软件最后都会落实到代码,而代码,其背后的架构设计或设计思想或模式固然重要,但我觉得更重要的东西则是良好的命名.混乱或错误的命名不仅让我们对代码难以理解,更糟糕的是,会误导我们的思维,导致对代码的理解完全错误.相反,良好的命名,则可以让我们的代码非常容易读懂,也能向读者正确表达事物以及逻辑的本质,从而使得代码的可维护性就大大增强. 另外一点也许大家还没感受到,那就是良好的命名,以及良好的命名习惯,由于我们总是对每个概念的名称要求非常苛刻,我们会思考这个名称所表达的概念是否正确,该名称是否正确表

怎样让你的代码更好的被JVM JIT Inlining

好书推荐:Effective Java中文版(第2版) JVM JIT编译器优化技术有近100中,其中最最重要的方式就是内联(inlining).方法内联可以省掉方法栈帧的创建,方法内联还使让JIT编译器更多更深入的优化变成可能.本人在fastxml(速度比XPP3(基于xmlpull)还快的xml解析器)开源项目中针对方法内联进行了很多学习和实践,这里总结一下,介绍一下怎么让你的代码更好的被JVM JIT Inlining. Inlining相关的启动参数 上一篇博客<Java JIT性能调优

mysql 利用触发器(Trigger)让代码更简单

一,什么触发器 1,个人理解 触发器,从字面来理解,一触即发的一个器,简称触发器(哈哈,个人理解),举个例子吧,好比天黑了,你开灯了,你看到东西了.你放炮仗,点燃了,一会就炸了. 2,官方定义 触发器(trigger)是个特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行.触发器经常用于加强数据的完整性约束和业务规则等. 触发器可以从 DBA_TRIGGERS ,USER_TRIGGER

怎么让你的Python代码更优雅!

3 个可以使你的 Python 代码更优雅.可读.直观和易于维护的工具. Python 提供了一组独特的工具和语言特性来使你的代码更加优雅.可读和直观.为正确的问题选择合适的工具,你的代码将更易于维护.在本文中,我们将研究其中的三个工具:魔术方法.迭代器和生成器,以及方法魔术. 加vx:tanzhouyiwan 免费领取Python学习资料 魔术方法 魔术方法可以看作是 Python 的管道.它们被称为"底层"方法,用于某些内置的方法.符号和操作.你可能熟悉的常见魔术方法是 __ini

为什么知乎上很多人都反对创业?(上战场的士兵如果先拿枪打打靶练练枪法,研究研究战术之后,战损比肯定要更好看一点)

为什么知乎上很多人都反对创业? 经常看到一些排名靠前的答案,内容无非规劝他人不要创业,要脚踏实地,认为党和国家大众创业万众创新的政策方针都是胡扯?难道知乎上的知识分子们竟比国家的智囊更有远见?还是他们的认识层次达不到? 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:唐缺链接:https://www.zhihu.com/question/37559307/answer/72755062来源:知乎 -------------------------------------

让你的自动化代码更健壮

在做自动化测试时,尤其是UI级自动化,如何让你的代码更健壮可能是你经常要考虑的问题,这里分享几个小Tips. 多用WaitForXXXX 严格意义上讲,任何长时间的“硬等待”都是可取的!!到处随意的Sleep()只能显示你技能的匮乏,写出的Case也会效率低下.所以当此之时, 我们都应该多用WaitFor方法,而且任何的自动化框架都是有类似方法的,像Robotium的: solo.waitForDialogToClose() //waits for the dialog to close sol

基于AOP的MVC拦截异常让代码更优美

与asp.net 打交道很多年,如今天微软的优秀框架越来越多,其中微软在基于mvc的思想架构,也推出了自己的一套asp.net mvc 框架,如果你亲身体验过它,会情不自禁的说‘漂亮’.回过头来,‘漂亮’终归有个好的思想,其中类似于AOP的思想,就在其中体现的淋漓尽致,今天本文主要讨论的是基于AOP思想构成的‘异常过滤器’.我们的目的只有一个,让try...catch...无处盾形,让代码更健壮优美. 一.理解mvc里filter是怎么运行的 老外的一篇文章是这样的草图 通过翻译中文是这样的 其

依赖注入——让iOS代码更简洁

原文链接:https://corner.squareup.com/2015/06/dependency-injection-in-objc.html 本文是自己通过阅读 Dependency Injection:Give Your iOS Code a Shot in the Arm 进行总结+翻译的,有错误之处请不吝啬的指出.下面是正文: 依赖注入可以通过初始化方法(或构造函数)传递所需要的参数,或者通过属性(setter)传递.这里将对这两种方法进行讲解. 初始化方法注入: - (insta

如何在Android开发中让你的代码更有效率

如何在Android开发中让你的代码更有效率 最近看了一个视频,名字叫做Doing More With Less: Being a Good Android Citizen,主要是讲如何用少少的几句代码来改善Android App的性能.在这个视频里面,演讲者以一个图片app为例讲解如何应用Android中现有的东西来改善app性能问题. 这个图片app的代码:https://github.com/penkzhou/iogallery.ppt:http://greenrobot.qiniudn.