iOS-Effective Objective-C 2.0 读书笔记(二)

哈哈,上一篇可以参考iOS-Effective Objective-C 2.0 读书笔记(一)。这一章主要是涉及对象、消息和运行期的一些相关特性。

第7条 在对象的内部尽量直接访问实例变量

  我们都知道使用了属性之后,可以通过存取方法来访问相关实例变量,也可以直接访问实例变量。如下所示:

@property (noatnomic, copy) NSString *userName

//直接访问
_userName = @"lysongzi";

//存取方法访问
self.userName = @"lysongzi";

然而这两种方式是有区别的。

  1. 由于不经过OC的“消息分发”步骤,所以直接访问实例变量的速度当然比较快。在这种情况下,编译器所生成的代码会直接访问保存对象实例变量的那块内存。
  2. 直接访问实例变量时,不会调用其setter方法,这就绕过了为属性定义的和“内存管理”相关的语义设置。比如说,如果在ARC下直接访问一个声明为copy的属性,那么并不会拷贝该属性,只会保留新值并释放旧值。
  3. 如果直接访问实例变量,那么不会触发KVO通知(这个KVO实现机制有关,参考iOS-KVC-KVO学习).这样做可能会产生安全问题,这还是要取决于具体的对象行为。
  4. 通过属性来访问有助于排查与之相关的错误,因为可以给setter或getter方法中添加断电,监控该属性的调用者及其调用时间。

  然后以下是作者给出的合适选择何种方式访问实例变量。大家参考参考就好,主要还是要取决与具体的需要把我觉得。

  1. 读取实例变量值,采用直接访问实例变量的方式。
  2. 设置实例变量值,采用setter方式设置新值。
  3. 一般情况下,在init方法中采用直接访问实例变量的方式。
  4. 在特殊情况下,比如说待初始化的实例变量声明在超类中,而我们又无法在子类中直接访问该实例变量时,就需要调用setter方法来设置新值了。
  5. 使用了lazy initialization的对象,使用getter/setter方式访问属性。

第9条 以”类族模式”隐藏实现的细节

  “类族模式”(class cluster)是一种很有用的模式,可以隐藏“抽象基类”背后的实现细节。比如iOS中UIKit库中的UIButton,提供了一个类方法,通过传入一个代表按钮类型的参数,已返回特定的UIButton对象实例。
  然而实际上一个类族是由多个从共同基类派生出来的子类组成的,由于他们派生自一个共同的抽象基类,所以他们都实现了同一套接口。
  在创建类族的方式中,最常用的方式就是使用“工厂方法”。(好吧这个问题面试时候被问过了,还好当时还记得工厂模式,非常幸运的蒙对了哈哈哈哈哈哈哈哈哈哈)下面我们举个简单的例子进行说明。

1.首先定义个抽象基类。

typedef NS_ENUM(NSUInteger, LYSEmployeeType)
{
    LYSEmployeeTypeDeveloper,
    LYSEmployeeTypeManager,
    LYSEmployeeTypeDesigner
};

@interface LYSEmployee : NSObject
@property (copy) NSString *name;
@property NSInteger salary;

+ (LYSEmployee *)employeeWithType:(LYSEmployeeType)type;

- (void)doWork;

@end

@implementation LYSEmployee

+ (LYSEmployee *)employeeWithType:(LYSEmployeeType)type
 {
     switch (type)
     {
         case LYSEmployeeTypeDeveloper:
             return [LYSEmployeeDeveloper new];
             break;
         case LYSEmployeeTypeDeveloper:
             return [LYSEmployeeManager new];
             break;
         case LYSEmployeeTypeDeveloper:
             return [LYSEmployeeDesigner new];
             break;
     }
 }

- (void)doWork
{
    //由子类来实现具体的接口操作
}

@end

2.然后每一个子类都继承自该基类,并实现其中定义的一些接口。

@interface LYSEmployeeDeveloper : NSObject
@end

@implementation LYSEmployeeDeveloper
- (void)doWork
{
    [self writeCode];
}
@end

  OC中没办法指明某个基类是“抽象的”。于是,开发者通常会在文档中写明类的用法。(= =,我突然在想可不可以结合协议来模拟一个抽象基类,这样子可以把接口都放到协议中,然所有子类都遵循这个协议)。

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

  额,这个的应用场景不好说明。这里主要就是简单介绍一下iOS中运行时的一个重要特性,就是关联对象。这个机制使得我们可以给某个对象关联许多其他对象,这些对象通过“键(key)”来区分。
  存取对象值时,可以指明“存取策略”,用以维护相应的“内存管理语义”。

OBJC_ASSOCIATION_ASSIGN             //assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC   //nonatomic, retain
OBJC_ASSOCIATION_COPY_NONATOMIC     //nonatomic, copy
OBJC_ASSOCIATION_RETAIN             //retain
OBJC_ASSOCIATION_COPY               //assign

iOS的运行时库中提供一下方法来管理关联对象。

//通过给定的键,给某对象关联一个对象值
//此方法可以通过给某个键设置一个nil的关联,可以看做是移除一个关联对象
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy);

//通过给定的键,从某对象中获取关联对象值
id objc_getAssociatedObject(id object, void *key);

//移除指定对象的全部关联对象
void objc_removeAssociatedObjects(id object);