Effective Objective -C 第二章 对象、消息、运行期(1) - 读书笔记
"对象"就是“基本构造单元”,开发者可以通过对象来存储并传递数据。
在对象之间传递数据并执行任务的过程就叫做"消息传递"。
"属性"在OC中的作用就是封装对象中的数据。OC对象通常会把其所有需要的数据保存为各种实例变量。实例变量一般通过“存取方法”来访问,“获取方法”用于读取变量值,而”设置方法“用于写入变量值。
- 属性特质
原子性:在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果设定属性具备nonatomic特质,则不使用同步锁。
读/写权限:
具备readwrite(读写)特质的属性拥有"获取方法(getter)"与"设置方法(setter)"。若该属性有@synthesize实现,则编译器会自动生成这两个方法。
具备readonly(只读)特质的属性仅拥有获取方法,只要当该属性有@synthesize实现时,编译器才会为其合成获取方法。
内存管理语义:
- assign "设置方法"只会执行针对"纯量类型"(例如:CGFlost或NSInteger等)的简单赋值操作。
- strong 次特质表明该属性定义了一种"拥有关系"。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。
- weak 次特质表明该属性定义了一种"非拥有关系"。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质通assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空。
- unsafe_unretained 此特质的语义和assign相同,但是他适用于"对象类型",该特质表达一种"非拥有关系",当目标对象遭到摧毁时,属性值不会自动清空,这一点与weak有区别。
- copy 此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其"拷贝"。
- getter=<name> 指定"获取方法"。
- setter=<name> 指定"设置方法"。
- 在对象内部尽量直接访问实例变量(在读取实例变量的时候建议尽量采用直接访问的形式,而在设置实例变量的时候通过属性来做)
// 用实例变量完成获取和设置方法
- 在对象内部读取数据是,应该直接通过实例变量来读,而写入数据时,则应通过属性来写。
- 在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据。
- 有时会使用惰性初始化技术配置某份数据,这种情况下,需要通过属性来读取数据。
- 理解"对象等同性"这样概念
- 按照==操作符比较出来的结果未必是我们想要的,因为该操作比较的是两个指针本身,而不是其所指的对象。应该使用NSObject协议中声明的"isEqual:"方法来判断两个对象的等同性。NSString类实现了一个自己独有的等同性判断方法,名叫"isEqualToString:"。传递给噶方法的对象必须是NSString,否则结果未定义(undefined)。调用该方法比调用"isEqual:"方法快,后者还要执行额外的步骤,因为它不知道受测对象的类型。
- NSObject类对这两个方法的默认实现是:当且仅当其"指针值"完全相等时,这两个对象才相等。
- 可以用方法-(NSUInteger)hash;也可以实现等同性的比较。根据等同性的约定:若两对象相等,则其哈希码(hash)也相等,但是两个哈希码相同的对象却未必相等。
- 特定类所具有的等同性判定方法 例如"isEqualToArray:"、"isEqualToDictionary:"。在编写判定方法时,也应一并覆写"isEqual:"方法。
- 等同性判定的执行深度。 NSArray的检测方式为先看两个数组所含对象个数是否相同,若相同,则在每个对应位置的两个对象身上调用其"isEqual:"方法。如果对应位置上的对象均相等,那么这两个数组就相等,这就是"深度等同性判断"。
- 容器中可变类的等同性。把某对象放入collection之后改变其内容将会造成collection中的值改变。
- 若想检测对象的等同性,请提供"isEqual:"与hash方法;相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同;不要盲目地逐个检测每条属性,而是应该依照具体需求来置顶检测方案;编写hash方法时,应该使用计算速度快而且哈希码碰撞几率低的算法。
- 以"类族模式"隐蔽实现细节
- "类族"是一种很有用的模式,可以隐蔽"抽象基类"背后的实现细节。
- 创建类族:
1 typedef NS_ENUM(NSUInteger,EmployeeType){ 2 EmployeeTypeDeveloper, 3 EmployeeTypeDesigner, 4 EmployeeTypeFinance 5 }; 6 7 @interface Employee : NSObject 8 9 @property (copy) NSString *name; 10 @property NSUInteger salary; 11 12 + (Employee *)employeeWithType:(EmployeeType){ 13 switch(type){ 14 case EmployeeTypeDeveloper: 15 return [EmployeeTypeDeveloper new]; 16 break; 17 case EmployeeTypeDesigner: 18 return [EmployeeTypeDesigner new]; 19 break; 20 case EmployeeTypeFinance: 21 return [EmployeeTypeFinance new]; 22 break; 23 24 } 25 } 26 - (void) doADaysWork{ 27 }
每个“实体子类”都是从基类继承而来。
1 @interface EmployeeDeveloper : Employee 2 3 @end 4 5 @implementation EmployeeDeveloper 6 - (void) diADaysWork{ 7 [self writeCode]; 8 } 9 10 @end
基类实现了一个“类方法”,该方法根据待创建的雇员类别分配好对应的雇员实例。这种“工厂模式”是创建类族的办法之一。
3. Cocoa 里的类族
例如 NSArray,NSDictionary,这都是类族。
给某个类族增加子类,需要注意的几条规则:
- 子类应该继承自类族中的抽象基类。例如 若要编写NSArray 类族的子类,则需要令其继承自不可变数组的基类或可变数组的基类。
- 子类应该定义自己的数据存储方式。子类必须用一个实例变量来存放数组中的对象。NSArray本身只不过是包在其他隐藏对象外面的壳,它仅仅定义了所有数族都需要具备的一些接口。
- 子类应当覆写超类文档中指明需要覆写的方法。
要点: 1. 类族模式可以把视线细节隐藏在一套简单的公共借口后面;
2. 系统框架中经常使用类族;
3. 从类族的公共抽象基类中继承子类时要当心,若有开发文档,则应首先阅读。
- 在既有类中使用关联对象存放自定义数据
有时需要在对象中存放相关信息,这时我们通常会从对象所属的类中继承一个子类,然后改用这个子类对象。关联对象:可以给某个对象关联许多其他对象,这些对象通过“键”来区分。

1. void objc_setAssociatedObject(id object, void * key,id value,objc_AssociationPolicy policy). 此方法以给定的键和策略为某对象设置关联对象。
2. void objc_getAssociatedObject(id object, void * key). 此方法根据给定的键从某对象中获取相应的关联对象值。
3. void objc_removeAssociatedObject(id object). 此方法移除指定对象的全部关联对象。
用法例子:
1 #import <objc/runtime.h> 2 3 static void *MyAlertViewKey = @"MyAlertViewKey"; 4 5 - (void)askUserAQuestion{ 6 7 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Question" 8 message:@"弄啥呢?" 9 delegate:self 10 cancelButtonTitle:@"Cancel" 11 otherButtonTitles:@"Continue",nil]; 12 13 void (^block)(NSInteger) = ^(NSInteger buttonIndex){ 14 if(buttonIndex == 0){ 15 [self doCancle] 16 } else{ 17 [self doContinue]} 18 }; 19 20 objc_setAssociatedObject(alert,MyAlertViewKey,block,BJC_ASSOCIATION_COPY); 21 [alert show]; 22 23 } 24 25 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ 26 void (^block)(NSInteger) = objc_getAssociatedObject(alertView,MyAlertViewKey); 27 block (buttonIndex); 28 }
- 可以通过“关联对象”机制来把两个对象连起来。
- 定义关联对象时可指定内存管理语义,用以模仿定义属性时所采用的“拥有关系”与“非拥有关系”。
- 只有在其他做法不可行时才应选用关联对象,因为这种做法通常会引入难以查找的bug.
浙公网安备 33010602011771号