iOS Developer

编写高质量iOS与OS X代码有效方法读书笔记(持续更新中)

2017-09-21  本文已影响48人  Claire_wu

1 类的头文件中尽量少引入其他头文件

//移至"class-continuation"分类中,而不是放在.h文件中
@interface YQCircleViewController ()<UISearchBarDelegate,UISearchDisplayDelegate>{
}

2 多用字面量语法,少用与之等价的方法

使用字面量语法可以缩减源代码长度,使其更加易读,减少代码出错机率。字面量语法实际是一种 “语法糖”,也称 “糖衣语法”,是指计算机语言中与另外一套语法等效但是开发者用起来却更加方便的语法。

//字面数值
NSNumber *someNumner = @1;
NSNumber *intNumner = @1;
NSNumber *floatNumner = @2.5f;
NSNumber *doubleNumner = @3.14159;
NSNumber *charNumner = @'s';

//字面量数组
NSArray *array = @[@"a",@"b"@"c"];
NSString *string = array[0];  //直接使用下标操作数组

//字面量字典
NSDictionary *dict = @{@"key":@"value"};
NSString *string = dict[@"key"];  //直接使用键来操作字典

//可变数组与字典
NSMutableArray *mutable = [@[@"a",@"b"] mutableCopy];

然后应该通过下标操作来访问数组或字典中的键来获取字典中的元素,不过注意语法糖语法的局限性:

3 多用类型常量,少用#define 预处理指令

示例:

image.png image.png

4 用枚举表示状态、选项、状态码

typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
    UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
    UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
    UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
    UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
    UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
    UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
    UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
} __TVOS_PROHIBITED;

5 在对象内部尽量直接访问实例变量

6 理解对象等同性的概念

7 在既有类中使用关联对象存放自定义数据

#import <objc/runtime.h>
static void *EOCMyAlertViewKey = @"EOCMyAlertViewKey";

- (IBAction)testAlertAssociation:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Quetion" message:@"" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"OK",nil];
    void (^block)(NSInteger) = ^(NSInteger buttonIndex) {
        NSLog(@"click index %i",buttonIndex);
    };
    objc_setAssociatedObject(alert, EOCMyAlertViewKey, block, OBJC_ASSOCIATION_COPY);
    [alert show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView, EOCMyAlertViewKey);
    block(buttonIndex);
}

8 尽量使用不可变对象

9 使用 “类扩展(class-continuation)”隐藏实现细节

10 为私有方法名加前缀

编写类的实现代码时,经常要写一些只在内部使用的方法,编码时应为这些方法加上某些前缀,如p_有助于调试,以便很容易的把公有方法和私有方法区分开,而且还便于修改且不影响已公开的公有API接口。但不要单用一个下划线_来做私有方法的前缀,这种做法是预留给苹果公司用的

11总是为第三方类的分类名称加前缀

12 将类的实现代码分散到便于管理的数个分类中

13 在dealloc方法中只释放引用并解除监听

14 多用块枚举,少用for 循环

15 构建缓存时选用 NSCache 而非 NSDictionay

16 以自动释放池降低内存峰值

程序中有时会遇到大量的for循环,而占用的这些内存需要到下一次事件循环才会被释放掉,适当的增加自动释放池可以降低内存峰值,因为在新增的这个池末尾,会将临时变量释放掉。

NSMutableArray *array = [[NSMutableArray alloc] init];
    for (NSInteger i=0; i<1000; i++) {
        @autoreleasepool {
            //加上自动释放池后,可以提早释放o这个临时变量
            NSObject *o = [NSObject new];
            [array addObject:o];
        }
    }

17 为常用的块类型创建typedef

typedef int(^EOCSomeBlock) (BOOL flag);
@property (nonatomic,copy) EOCSomeBlock block;
typedef void(^ACAccountStoreSaveCompletionHandler)(BOOL success, NSError *error);
typedef void(^ACAccountStoreRemoveCompletionHandler)(BOOL success, NSError *error);
typedef void(^ACAccountStoreRequestAccessCompletionHandler)(BOOL granted, NSError *error);

18 多用GCD,少用perfromSelector的方法

19 不要使用dispatch_get_current_queue

20 熟悉系统框架

21 精简intialize与load方法的实现代码

21.1 load

对于加入运行期系统中的每个类class及分类category来说,一定会调用load方法,而且仅调用一次。比如app启动时,肯定会执行一次,但是在执行该方法时,运行期系统处于“脆弱状态”(fragile state),在执行子类的load方法时,必先执行所有超类的load方法,如果代码还依赖了其他程序类库,那么程序库相关类的load方法也必定会先执行。然而给定一个程序库,是无法判断出各个类的加载顺序,因此在类的load方法里调用其他的类是不安全的

+ (void)load {
    NSLog(@"Loading EOCClassB");
    //在EOCClassB类的load方法里调用EOCClassA,但此时却不确定EOCClassA是否已经load完毕
    EOCClassA *o = [EOCClassA new];
}

还有个重要的事情要注意,load方法不像普通方法遵循继承的规则,如果本类没有实现load方法,那么不管其各级超累是否实现load方法都不会执行。分类和其所属的类都可能实现load方法,那么类一定比分类的load方法先执行。而且load方法务必实现的精简一些,因为整个应用程序在执行load方法时都会阻塞。
实际上,凡是想通过load在类加载之前执行某些任务的,基本都不太对,笔者目前自己见过最多的便是在load方法里写方法交换逻辑。

21.2 initialize

如果想执行与类相关的初始化操作,还有个办法就是覆写initialize方法。initialize方法是当程序运用到了相关类,才会调用,且只调用一次,如果某个类一直没有用则initialize方法一直不会被执行,就是所谓的“惰性调用”。initialize方法执行时,运行期系统是处于正常状态的,从运行期系统完整度调用来说可以调用任何类的任意方法。而且运行期系统能确保initialize方法一定会在线程安全环境中执行,因此只有执行initialize方法的线程可以操作类或者实例,其他的线程全部都要先阻塞。
initialize方法遵循继承规则,如果某个类没有实现它,而其超类实现了,那么就会运行超类的实现代码。
initialize方法也要尽量保持精简,如果在initialize方法里调用了其他类,如果这个类没有被初始化,那么此时系统会迫使其初始化,但如果这个类又应用了本类(互相依赖),但本类此时还没初始化完,则会出现问题。
总结来说:

static void *EOCMyAlertViewKey = @"EOCMyAlertViewKey";

//NSMutableArray在这里初始化编译器会报错
static NSMutableArray *ksomeObjects;         //= [NSMutableArray new];

typedef int(^EOCSomeBlock) (BOOL flag);

@interface ViewController ()<UIAlertViewDelegate>{
}
@property (nonatomic,copy) EOCSomeBlock block;
@end

@implementation ViewController
+ (void)initialize {
    //放在这里初始化
    ksomeObjects = [NSMutableArray new];
}
@end

22 通过委托与数据源协议进行对象间通信

上一篇下一篇

猜你喜欢

热点阅读