- Effective Objective-C 2.0笔记(二)
- Effective Objective-C 2.0笔记(一)
- Effective Objective-C 2.0笔记(三)
- Effective Objective-C 2.0笔记(五)
- Effective Objective-C 2.0 再读笔记(三
- Effective Objective-C 2.0 再读笔记(四
- 《Effective Objective-C 2.0》读书/实战
- 《Effective Objective-C 2.0 》 阅读笔
- Effective Objective-C 2.0 无废话精简篇
- Effective Objective-c 2.0 学习笔记
Effective Objective-c 2.0 学习笔记
编写高质量iOS与OS X代码的52个有效方法
第一章 熟悉Objective-C
第1条 了解Objective-C语言的起源
-
笔记:
-
OC是动态语言,使用
消息结构
(messaging structure),而非函数调用
(function calling)。OC由消息语言的鼻祖smalltalk演化而来。 -
区别:消息结构的语言,其运行时执行的代码由运行环境决定;函数调用的语言,则由编译器决定。
-
OC中对象所占内存总是分配在
堆空间
(heap space)而不会在栈空间
(stack)上 -
OC中不带*的变量,有可能会使用
栈空间
,比如CGRect 结构体
~~~~ struct CGRect{ ~~ CGPoint point; ~~ CGSize size; ~~ }; ~~
因为创建对象需要额外开销(分配&释放堆内存),从效率上,结构体性能更好。 -
要点
-
Objective-c为C语言天减了面向对象特性,是其超集。Objective-C是使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收这一消息后,究竟该执行何种代码实现,有运行期环境决定而非编译器。
-
理解C语言的核心概念有助于写好Objective-C程序,尤其是掌握内存模型与指针。
第2条 在类的头文件中尽量少引用其他头文件。
-
笔记:
-
头文件导入时,如非使用者必须知道,尽量将头文件的导入时机延后(头文件的导入反倒.m文件中),这样会大大减少编译时间,减少头文件之间相互依赖的复杂度。
-
前向声明---
@class ClassName
这种头文件的声明方式(或者引入方式)叫做前向声明
(或者向前声明)--- (forward declaring);前向声明方式引入的头文件,不会在编译期间进行编译,只是向声明我这里有这么各类,你知道有就行了,不需要你编译。这种方式还能解决头文件相互包含的问题。 -
要点:
-
除非有必要条件,否则不要引入头文件。一般来说,应在某各类的头文件中使用前向声明来提及别的类,并在实现文件中引入哪些类的头文件。这样做可以尽量降低类之间的
耦合
(coupling) -
有时无法使用前向声明,比如要声明某个类遵循一项协议。这情况下,尽量把“该类遵循某协议”的这条生命移至"class-continuation分类"中。如果不行的话,就把协议单独放在一个头文件中,然后将其引入。
第3条 多用字面量语法,少用与之等价的方法
~~ ~~ //字面量语法 ~~ NSString *str1 = @"Kael"; ~~ ~~ //传统方式 ~~ NSString *str2 = [[NSString alloc] initWithFormat: @"Kael"]; ~~
- 笔记:
- 字面量语法(literal syntax)能极大的缩短代码长度,使其更易读。
- 字面数值 传统方式:
NSNumber *someNumber = [NSNumber numberWithInt:1]
NSNumber *intNumber = @1
NSNumber *floatNumbser = @0.125f
NSNumber *doubleNum = @3.14159
NSNumber *boolNum = @YES
NSNumber *charNum = @'abc'
- 字面量数组 传统方式:
NSArray *animals = [NSArray arrayWithObjects:@"cat",@"dog",@"mouse",nil];
NSArray *animals = @[@"cat",@"dog",@"mouse"];
NSString *dog = animals[1];
-
replaceObjectAtIndex:index
->mutableArray[1] = @"dog";
- 字面量字典 传统方式:
NSDictionary *personData = [NSDictionary dictionaryWithObjectsAndkeys:@"kael",@"name",@"age",@"18",nil];
NSDictionary *personData = @{@"name":@"kael",@"age":@"18"}
NSString *name = personData[@"name"];
-
setObject:obj forKey:key
->mutableDic[name] = @"kaelinda";
- 要点:
- 字面量语法创建 字符串、数值、数组、字典 比常规方法更加简明扼要。
- 应该通过取下标方式来取 字典、数组 中的值。
- 用字面量语法创建数组或者字典时,若值中有nil,则会抛出异常。因此,务必确保值中不含nil。
第4条 多用类型常量,少用#define 预处理命令
- 笔记:
- 宏定义等用于处理命令 是在编译器 编译期间 将定义的 字符替换,所以这样的缺点是 不能很好的从宏定义上读出常量的类型信息。
static const NSTimeInterval kAnimationinDuration = 0.3;
-
extern NSString *const EOCString;
比如 通知等需要有一个外部可见的变量作为 通知名
- 要点:
- 不要用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器编译期间,已经把这些值写死了,如果有人对常量改动,编译器也不会产生警告信息;
- 在实现文件中(.m文件)使用
static const
来定义只在编译单元可见(只在.m文件中可见,不暴露给外部使用)的常量。因此常量不在全局符号表中,命名上不需要在名称上加前缀。 - 在头文件中用
extern
来声明全局常量,并在相关实现文件中定义其值。
建议:处理隐藏的返回类型,优先选择实例类型而非id类型
- 笔记:
-
instancetype
仅仅用作Objective-C方法的返回类型 -
instancetype
有类型检测,更安全
*
建议:尽量使用模块方式与多类建立复合关系
-
#import
和#include
其根本就是简单的复制、粘贴,将.h文件中的内容一字不落的复制到当前文件中,#import
可避免头文件的重复引用。 - 以预编译头文件的方式,虽然可以缩短编译时间,但维护棘手,不利于广泛应用。
- 模块功能,其应用不仅仅变现与编译的速度加快,同事在链接框架等方面也非常好用。
- 启动模块功能后,编译器会隐式的把所有的
#import
转换成@import
高度警惕空指针和野指针的袭击
- 笔记:
- nil、Nil、NULL的区别:
nil:指向oc中对象的空指针,针对对象。
Nil:指向oc中类的空指针,针对类。
NULL:指向其他类型的空指针,如一个c类型的内存指针,基本数据类型为空,基本类型。
NSNull:在集合对象中,表示空值的对象。
- 要点:
- 空指针(NULL)指的是没有存储任何内存地址的指针;野指针,是指向“垃圾内存”(不可用内存)的指针。
- 利用野指针发消息是很危险的,会报错。也就是说,一个对象被回收了,就不要再去操作它,不要尝试给它发消息。
- 利用空指针发消息是没有任何问题的,也就是说代码是没有错误的。
清楚长两字符串和一般字符串的区别
~~ ~~ NSString *string1 = @"hello"; ~~ NSString *string2 = @"hello" ~~ if (strng1 == string2){ ~~ NSLog(@"They are same address"); ~~ }; ~~ ~~ NSString *string3 = [NSString alloc]; ~~ nsstring *string4 = [string3 initWithString:string1]; ~~ if (string3!=string4){ ~~ NSLog(@"string3 is not same to string4"); ~~ } ~~ ~~ if (string1 == string4){ ~~ NSLog(@"string1 is same to string4"); ~~ } ~~ ~~
- 由于编译器的优化,相同内容的常量字符串的地址是完全相同的。
- 如果用常量字符串来初始化一个字符串,那么这两个字符串也将是相同的常量。
- 对常量字符串永远不要release,因为多次release都还能访问。
第5条 用枚举表示状态、选项、状态码
- 笔记:
- 想要将多个枚举能同时生效(例如,屏幕的方向),那需要枚举值设置为 2 的n次幂;
- 还可以通过按位操作
- NS_ENUM与NS_OPTIONS区别:
- NS_ENUM枚举项的值为NSInteger,NS_OPTIONS枚举项的值为NSUInteger;
- NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举
- NS_OPTIONS的枚举项的值需要像这样表示1 << 0,1 << 1,2的几次方这样,而NS_ENUM可以直接给像1,2,3这样
- 要点:
- 在处理枚举类型的switch语句时,不要使用default分支,这样加入新枚举时编译器会提醒开发者:switch语句并未处理所有枚举。
第二章
第6条 理解属性这一概念
- 笔记:
- 属性(property) = 成员变量 + getter + setter
- 创建属性后,编译器会自动生成setter、getter属性
属性特质:
'' 1. 原子性:nonatomic 线程不安全;atomic 原子性,通过线程锁定等保证其原子性;
'' 2. 读/写权限:readwrite———可读可写(setter、getter);readonly———只读。
'' 3. 内存管理语义:assign(弱引用)、weak(弱引用)、strong(强引用)、copy(强引用)、unsafe_unretained(针对于对象的 类似于assign)
'' 4. 方法名:
'' - getter=<name>
--> 指定“获取方法”的方法名 @property (nonatomic,getter=isOn)BOOL on;
'' - seeter=<name>
--> 指定设置方法的方法名 @property (nonatomic,getter=isOn,seeter=setBaseModelName)BOOL on;
第7条 在对象内部尽量直接访问实例变量
- 直接访问实例变量,不会走
方法派送
(method dispatch);直接访问实例变量速度上要快得多。 - 直接访问实例变量,不会调用“设置方法”(setter),ARC下copy修饰的属性,就不会copy该属性,只会保留新值并释放旧值。
- 直接访问实例变量,不会触发“键值观察(Key-ValueObserving,KVO)”
- 通过属性访问,可以通过setter、getter增加断点监控键值变化。
折中:设置变量时走setter方法;获取变量值时直接访问实例变量;
注意:
- 初始化方法(或者 dealloc)中设置属性值的时候,应该直接访问实例变量,因为子类可能会
覆写
该方法; - 惰性初始化来配置数据,这时候需要通过属性来读取数据。
第8条 理解“对象等同性”这一概念
- 笔记:
对象等同性比较:
-
==
比较的是指针本身 -
isEqual:
方法比较的是指针指向的内存地址
NSobject协议中有两个判断等同性的关键方法:
~~ ~~ -(BOOL)isEqual:(id)object; ~~ -(NSUInteger)hash; ~~
- 要点:
- 相同的对象必须具有相同的哈希码;
- 拥有相同哈希码的对象不一定相同;
第9条 以“类族模式”隐藏实现细节
1.笔记:
- 基类 + 分工的子类
- 子类都应当继承自类族中的抽象基类。
- 子类应该定义自己的数据存储方式。
- 子类应当覆写超类文档中指明需要覆写的方法。
2.要点:
- 类族模式可以把实现细节隐藏在一套简单的公共接口后面。
- 系统框架中经常使用类族。
- 从类族的公共抽象基类中集成子类是要当心,若文档有变化,则应当先阅读。
网友评论