注:转载请附上原作者及其链接
第一条:了解Objective-C语言起源
Smalltalk 消息型语言的鼻祖
函数调用语言:由编译器决定
消息结构语言:由运行环境决定
“动态绑定”在运行时才会检查对象类型
“运行期组件”
指针 32位计算机4字节 64位计算机8字节
相比C语言,Objective-C将堆内存管理抽取出来,不需要用malloc和free来分配或释放对象所占内存 — 内存管理架构(引用计数)
例如 CGRect 这种结构体如果改用Objective-C对象来做的话,性能会受影响:创建对象需要额外开销—分配释放堆内存等
结构体分配的内存在栈上,不需要上层管理
第二条:在类的头文件中尽量少引入其他头文件
C C++ OC 头文件+实现文件
这也是编程思想的一种体现,可以说.h和.m文件时完全独立的,只是为了要求有较好的可读性,才要求两个文件的文件名一致,这也是把接口和实现分离,让调用者不必去关心具体的实现细节。
向前声明 @class 告诉编译器某个类存在
第三条:多用字面量语法(即“语法糖”)
-
数值
NSNumber *num = [NSNumber numberWithInt:1 ];
// 语法糖形式:
NSNumber *num = @"1";
-
数组
NSArray *animals = [NSArray arrayWithObjects:@"cat",@"dog",nil];
// 语法糖形式:
NSArray *animals = @[@"cat",@"dog"];
取元素
NSString *dog = [animals objectAtIndex:1];
// 语法糖形式
NSString *dog = animals[1];
一个小细节:
-
字典
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"ma":@"firstName",
@"yun":@"secondName",
[NSNumber numberWithInt:28:@"age",
nil]];
// 语法糖形式:
NSDictionary *dict = @{
@"firstName":@"ma",
@"secondName":@"yun",
@"age":@28
};
-
可变数组和可变字典
取元素替换元素
[mutableArray replaceObjectAtIndex:1 withObject:@"dog"];
[mutableDict setObject:@"hello" forKey:@"lastName"];
// 语法糖
mutableArray[1] = @"dog";
mutableDict[@"lastName"] = @"hello" ;
NSMutableArray *arr = [@[@"1",@"2"] mutableCopy];
第四条:多用常量 ,少用宏
比如把一个动画的播放时长提取出来,如果使用宏有个缺点-没有类型信息
#define ANIMATION_DURATION 0.3;
static const NSTimeinterval kAnimationDuration = 0.3;
第六条:理解“属性”这一概念(理解@property)
实例变量
和@property
@property可以分为四类:
- 原子性
- 读写权限
- 内存管理语义
- 方法名
第七条:在对象内部尽量直接访问实例变量
第十一条:对象消息 传递 机制
静态绑定:C语言采用的方式
动态绑定: OC采用的方式
选择器(选择子)
消息:选择器+参数
动态消息派发系统:编译器调用objc_msgSend
将消息转为标准的C语言函数调用
// 消息机制中的核心函数
objc_msgSend
// 边界情况处理函数
objc_msgSend_stret
objc_msgSend_fpret
objc_msgSendSuper
大致过程:
id returnValue = [someObject messageName:parameter:];
// 编译器转换为如下函数
id returnValue = objct_msgSend(someObject,
@selector(messageName:),
parameter);
// objct_msgSend会在接收者所属类中的“方法列表”中查找方法实现
// 查找会有缓存 -- 快速映射表
第十二条:对象消息 转发 机制
对象在收到无法解读的消息之后怎么办?
动态方法解析(所属类能否动态添加方法来处理选择器) --> 看有无“备援接收者” --> 启动完整的消息转发机制
动态方法解析:
+ (BOOL)resolveInstanceMethod:(SEL)selector; // 未实现的方法是示例方法
+ (BOOL)resolveClassMethod:(SEL)selector;// 未实现的方法是类方法
第十四条:理解“类对象”的用意
第三章 接口和API设计
第十五条:用前缀避免命名空间冲突
OC没有命名空间机制,所以我们应该设法避免命名冲突,否则很容易重名,命名冲突会在链接的时候报错
- 苹果宣称保留两个字母前缀使用的权利,建议使用三个字母(感觉没有啥必要,前缀起名稍微注意一下就可以了);
- 在将代码发布为程序库的时候尤其要注意给函数等加上前缀,否则很容易造成命名冲突而报“重复符号错误”,代码中用到了其他第三方库也要将第三方库的前缀一并修改统一;
第十六条:提供“全能初始化方法”
EOCSquare和ECORectangle例子
// 抛出异常
- (id)initWithWidth:(float)width andHeight:(float)height{
@throw[NSException exceptionWithName: NSInternalInconsistencyException
reason:@"Must be initWithDimension:instead.",
userInfo:nil];
}
第十七条:实现description方法
在构建需要打印到日志的字符串的时候,object会收到description消息
- 把待打印的信息放在字典里
-
description 和 debugDescription
第十八条:尽量使用不可变对象(即readonly的应用场景)
第十九条:命名规范
首先OC方法的冗长也是有好处的,可以清晰表达出意图,对比其他语言:
NSString *newText = [text stringByReplacingOCcurrenceOfString:@"fox"
withString:@"cat"];
// java中
string newText = text.replace("fox","cat");
-
方法命名
错误示例:
CYRectangle *rectangle = [[CYRectangle alloc] initWithSize:5.0f :10.0f];
正确示例:
CYRectangle *rectangle = [[CYRectangle alloc] initWithWidth:5.0f andHeight:10.0f];
第二十条:为私有方法加前缀
@interface CYObject : NSObject
- (void)publicMethod;
@end
@implemention CYObject
- (void)publicMethod{
}
// 加前缀
- (void)p_privateMethod{
}
@end
原因:
- 便于区分公共方法和私有方法;
- 公共API一般不要轻易改名字,改了可能别的开发者要更新代码,给私有方法加前缀很容易看出哪些方法可以随意改动,哪些方法不可以;
注意:苹果公司喜欢用一个单一的下划线作为私有方法的前缀,所以要求开发者不应该使用作为私有方法的前缀,否则在继承的时候无意的覆盖了父类的方法