一 熟悉Objective-C
- 了解Objective-C语言的起源
- 在类的头文件中尽量少引入其他头文件
- 除非确有必要,否则不要引入头文件。要在某个类的头文件中提及别的类(头文件不涉及被引用类的方法和变量)使用@class。
- 多用字面量语法,少用与之等价的方法
- 应使用字面量语法创建字符串、数值、数组、字典。
NSNumber *someNumber = @1;
NSArray *animals = @[@"cat",@"dog",@"mouse",@"badger"];
NSDictionary *personData = @{@"firstName" : @"Matt",@"lastName" : @"Galloway",@"age" : @28}; - 应通过取下标操作来访问数组下标或字典中的键所对应的元素。
NSString *dog = animals[1];
NSString *lastname = personData[@"lastName"]; - 用字面量语法创建数组或字典时,若值中有nil则会抛出异常,使字面量语法更为安全。
- 多用类型常量,少用
#define
预处理指令
不要用预处理指令
#define
定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换。即使有人重新定义了常量值,编译器也不产生警告。(#define
能定义函数)
#define ANIMATION_DURATION 0.3在.m中使用static const来定义“只在编译单元内可见的常量”,常用的命名法是在常量前面加字母k。
//EOCAnimatedView.m
#import "EOCAnimatedView.h"
static const NSTimerInterval kAnimationDuration = 0.3;-
在.h中使用extern来声明全局常量,并在相关.m中定义其值。这种常量要出现在全局符号表中,其名称应加以区隔,通常用与之相关的类名做前缀。
//EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
//EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;//in the header file extern NSString *const EOCStringConstant; //in the implementation file NSString *const EOCStringConstant = @"VALUE";
- 用枚举表示状态、选项、状态码
二 对象、消息、运行期
- 理解“属性”这一概念
- 使用
@dynamic
关键字阻止编译器自动合成存取方法,它会告诉编译器不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。 - 属性特质
非对象类型用assign
属性对象用strong
控件用weak
字符串NSString
用copy
- 开发iOS程序时应使用nonatomic属性,因为atomic属性会严重影响性能。
- 在对象内部尽量直接访问实例变量
- 在对象内部读取数据时,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
- 理解“对象等同性”这一概念
NSString *foo = @"Badger 123";
NSString *bar = [NSStringWithFormat:@"Badger %i",123];
BOOL equalA = (foo == bar); //< equalA = NO
BOOL equalB = [foo isEqual:bar]; //< equalB = YES;
BOOL equalC = [foo isEqualToString:bar]; //<equalC = YES;
-
==
表示指向同一块内存
isEqualToString:
比调用isEqual:
方法快
- 以“类族模式”隐藏实现细节
- 在既有类中使用关联对象存放自定义数据
- 理解objc_msgSend的作用
-
理解消息转发机制
- 用“方法调配技术”调试“黑盒方法”
- 理解“类对象”的用意
-
isMemberOfClass:
能够判断对象是否为某个特定类的实例,而isKindOfClass:
能够判断出对象是否为某类或其派生类的实例。
三 接口与API设计
- 用前缀避免命名空间冲突
- Apple宣称其保留使用所有“两字母前缀”(two-letter prefix)的权利,所以你自己选用的前缀应该是三个字母。
- 提供“全能初始化方法”
- 实现
description
方法
-
实现
description
方法返回一个有意义的字符串,用以描述该实例。
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic,copy,readonly) NSString *firstName;
@property (nonatomic,copy,readonly) NSString *lastName;
-(id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
@end@implementation EOCPerson -(id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName{ if ((self = [super init])) { _firstName = [firstName copy]; _lastName = [lastName copy]; } return self; }
该类的的description
方法通常可以这样实现:
-(NSString *)description{
return[NSString stringWithFormat:@"<%@: %p,"%@ %@">",[self class],self,_firstName,_lastName];
}
输出如下格式信息:
EOCPerson person = [[EOCPerson alloc] initWithFirstName:@"Bob" lastName:@"Smith"];
NSLog(@"person = %@",person];
//output
person=<EOCPerson: 0x7fb249c030f0,"Bob Smith">
或者用NSDictionary
类的description
方法
- (NSString)description {
return [NSString stringWithFormat:@"<%@: %p, %@>",
[self class],
self,
@{@"title":_title,
@"latitude":@(_latitude),
@"longitude":@(_longitude)}
];
}
- 尽量使用不可变对象
- 使用清晰而协调的命名方式
- 方法名和变量名使用“驼峰式大小写命名法”(camel casing)——以小写字母开头,其他单词首字母都大写。类名也用驼峰命名法,不过首字母要大写,且前面通常还有两三个前缀字母。方法名不要使用缩略后的类型名称。
- 为私有方法名加前缀
- 理解Objective-C错误模型
- 理解NSCopying协议
四 协议与分类
- 通过委托与数据源协议进行对象间通信
- 将类的实现代码分散到便于管理的数个分类之中
- 总是为第三方的分类名称加前缀
- 勿在分类中声明属性
- 使用“class-continuation分类”隐藏实现细节
- 通过协议提供匿名对象
- 具体的对象类型可以淡化成遵从某协议的
id
类型,协议里规定了对象所应实现的方法。 - 使用匿名对象来隐藏类型名称(或类名)。
五 内存管理
- 理解引用计数
- 以ARC简化引用计数
- 在
dealloc
方法中只释放引用并解除监听 - 编写“异常安全代码”时留意内存管理问题
- 以弱引用避免保留环
- 以“自动释放池”降低内存峰值
- 用“僵尸对象”调试内存管理问题
- 不要使用
retainCount
六 块与GCD
- 理解“块”这一概念
- 块是C、C++、Objective-C中的词法闭包。
- 块可接受参数,也可返回值
块类型的语法结构:return_type (^block_name)(parameters)
下面所定义的块,返回int值,并接受两个int做参数
int (^addBlock)(int a,int b)=^(int a,int b){
return a+b;
};
int add = addBlock(2,5); //< add=7
在声明块的范围里的全部变量,都可以为其所捕获,也就是说在块里依然可用。以下代码就使用了块以外的变量:
int additional = 5;
int (^addBlock)(int a,int b)=^(int a,int b){
return a+b+additional;
};
int add = addBlock(2,5); //< add=12
默认情况下,为块所捕获的变量,是不可以在块里修改的。在本例中,假如块内的代码改动了additional变量的值,那么编译器就会报错。不过,声明变量的时候可以加上__block
修饰符,就可以在块内修改了。
- 为常用的块类型创建typedef
- 以typedef重新定义块类型,可令块变量用起来更加简单
typedef int(^EOCSomeBlock)(BOOL flag,int value);
EOCSomeBlock block = ^(BOOL flag,int value){
//Implementation
};
- 用handler块降低代码分散程度
- 用块引用其所属对象时不要出现保留环
- 多用GCD,少用同步锁
- 多用GCD,少用
performSelector
系列方法
- 延后执行某项任务
dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0*NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
[self doSomething];
});
把任务放在主线程上执行
dispatch_async(dispatch_get_main_queue(), ^(
[self doSomething]
));
- 掌握GCD及操作队列的使用时机
- 在解决多线程与任务管理问题时,GCD并非唯一方案(
NSOperationQueue
)
- 通过
Dispatch Group
机制,根据系统资源状况来执行任务 - 使用
dispatch_once
来执行只需运行一次的线程安全代码 - 不要使用
dispatch_get_current_queue
七 系统框架
- 熟悉系统框架
- 多用块枚举,少用for循环
- 遍历collection有四种方式,最基本是
for
循环,其次是NSEnumerator
遍历法及快速遍历法(for in),最新最先进的方式则是“块枚举法”
NSArray
-(void)enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop)block
NSDictionary
-(void)enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop)block
- 对自定义其内存管理语义的collection使用无缝桥接
- 构建缓存是选用
NSCache
而非NSDictionary
- 精简initialize与load的实现代码
- 别忘了
NSTimer
会保留其目标对象