编写高质量的iOS代码(二)

2016-06-28  本文已影响66人  Mustard_Buli

理解“属性”这一概念

@interface MSTPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property (nonatomic, readwrite, copy) NSString *firstName;
@property (nonatomic, getter=isOn) BOOL on;

在对象内部尽量直接访问实例变量

 - (MSTCar *)myCar{
      if (!_myCar) {
          _myCar = [MSTCar alloc] init];
      }
       return _myCar;
 }

理解“对象等同性”(equality)这一概念

NSString *strA = @"Mustard 123";
NSString *strB = [NSString stringWithFormat:@"Mustard %i", 123];
BOOL equalA = (strA == strB);    //NO
BOOL equalB = [strA isEqual:strB];    //YES
BOOL equalC = [strA isEqualToString:strB];    //YES

NSString有一个独有的方法"isEqualToString:",同样的NSArray和NSDictionary也有类似的方法:"isEqualToArray"和"isEqualToDictionary"。调用本类的等同性判断方法相对于"isEqual:"更快一点,因为后者还需要判断受测对象的类型是否一致。

举个简单的例子,每个MSTPerson类都有四个属性:firstName、lastName、age、identifier。现在需要判断两个person是否相等,只需要判断identifier是否相等,而不需要进行“深度等同性判定”(deep equality,就是比较每个属性是否相等)。


以“类族模式”(class cluster pattern)隐藏实现细节

比如UIKit框架中的UIButton类。想创建一个button,需要调用下面这个“类方法”(class method):

 + (UIButton *)buttonWithType:(UIButtonType)type;

这个方法返回的对象取决于用户传入的button type。然而,不管返回的是什么类型的button,都是继承于同一个基类:UIButton。这么做的意义就是,使用者不需要关心创建出来的button具体是哪个子类,也不需要去考虑button绘制方式等实现细节。使用者这需要明白如何设置"title"这样的属性就可以了。
但是回过头来说,也可以这样写:

 - (void)drawRect:(CGRect)rect {
      if (_type == TypeA) {
          //Draw TypeA button
      } else if (_type == TypeB) {
          //Draw TypeB button
      } /* ... */
 }

这样看上去还算简单,但是需要依照button type来切换的绘制方法会有许多种,这样就变得特别的麻烦。所以应该将这种代码重构为多个子类,把各种button所用到的绘制方法放到相关的子类中去。但是这么做有一个小的弊端,就是使用者需要知道各种子类才行。虽然这样,但是这个小的弊端几乎可以说是忽略不计。

这里有一点需要注意的是:在创建实例的时候,你可能觉得自己创建的是某个类的实例,然而实际上创建的确实其子类的实例。比如下面这段代码:

typedef NS_ENUM(NSUInteger, MSTEmployeeType) {
      MSTEmployeeTypeDeveloper,
      MSTEmployeeTypeDesigner,
      MSTEmployeeTypeFinance
};
@inerface MSTEmployee : NSObject
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSUInteger salary;
//用来创建Employee实例
 + (MSTEmployee *)employeeWithType:(MSTEmployeeType)type;
//Employee每天的工作
 - (void)doADayWork;
@end
@implementation MSTEmployee
 + (MSTEmployee *)employeeWithType:(MSTEmployeeType)type {
      switch (type) {
          case MSTEmployeeTypeDeveloper:
              return [[MSTEmployeeDeveloper alloc] init];
              break;
          case MSTEmployeeTypeDesigner:
              return [[MSTEmployeeDesigner alloc] init];
              break;
          case MSTEmployeeTypeFinance:
              return [[MSTEmployeeFinance alloc] init];
              break;
      }
 }
 - (void)doADayWork {
      //子类来实现具体的工作
 }
@end

在上面这个例子中,[employee isMemberOfClass:[MSTEmployee class]]似乎返回的是YES,但是实际上返回的趋势NO,因为employee并非Employee类的实例,而是其某个子类的实例。

像上面所说的UIButton就是一个例子。大部分collection类都是类族,例如NSArray与其可变版本NSMutableArray。
在类族中实现子类是需要遵循的规范,一般都会定义于基类的文档中,编码前应该先看看。下面,列出了新增子类的时候需要遵守几条规则:

若要编写NSArray类族的子类,则需令其继承自不可变数组的基类或可变数组的基类。

子类必须用一个实例变量来存放数组中的对象。NSArray本身只不过是包在其他隐藏对象外面的壳,它仅仅定义了所有数组都需具备的一些接口。对于这个自定义的数组子类来说,可以用NSArray来保存其实例。

每个抽象基类中,都有一些子类必须覆写的方法。🌰:想要编写NSArray的子类,就需要实现count及"objectAtIndex:"方法。


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

//此方法以给定的键和策略为某对象设置关联对象值
 void objc_setAssociatedObject (id object, void *key, id value, objc_AssociationPolicy policy)
//此方法以给定的键从对象中获取相应的关联对象值
id objc_getAssociatedObject (id object, void *key)
//此方法移除指定对象的全部关联对象
void objc_removeAssociatedObjects (id object)

我们可以把某对象想象成NSDictionary,把关联到该对象的值理解为字典中的条目。于是,存取关联对象的值就相当于在NSDictionary对象调用[object setObject:value forKey:key]与[object objectForKey:key]方法。但是,两者之间有个重要差别:设置关联对象是用的键(key)是个“不透明的指针”(opaque pointer)。在设置关联对象值时,通常使用静态全局变量做键。

上一篇 下一篇

猜你喜欢

热点阅读