【Effective Objective-C】第一章

在类的头文件中尽量少引入其他头文件

与C++一样,Objective-C也是用“头文件”与“实现文件”来区隔代码。用Obejctive-C语言编写“类”的标准方式为:以类名做文件名,分别创建两个文件,头文件后缀用.h,实现文件后缀用.m。创建好一个类之后,其代码看上去如下所示:

//EOCPerson.h
#import <Foundation/Foundation.h>

@interface EOCPerson: NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end

//EOCPerson.m
#import "EOCPerson.h"

@implementation EOCPerson
// Implementation of methods
@end

但是有一种情况,比如你现在需要创建一个新类,名为EOCEmployer,在EOCPerson类的.h文件中声明一个EOCEmployer *类型的属性,那么,常规的做法是在EOCPerson.h文件中引用EOCEmployer.h文件#import "EOCEmployer.h"但是,这种办法不够优雅,我们有一种方法叫做“向前声明”。代码如下:

//EOCPerson.h
#import <Foundation/Foundation.h>

//此方法即为“向前声明”(可以实现不用引用该类的头文件就可以创建该类的属性)
@class EOCEmplyer;

@interface EOCPerson:NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
//创建的EOCEmployer类型属性
@property (nonatomic, strong) EOCEmployer *employer;

@end

接下来,如果需要使用EOCEmployer类的头文件去实现某些东西,就可以在EOCPerson类的实现文件(.m文件)中引用EOCEmployer.h头文件,代码如下:

//EOCPerson.m
#import "EOCPerson.h"
#import "EOCEmployer.h"

@implementation EOCPerson
// Implementation of methods
@end

次方法旨在将引入头文件的时机尽量延后,自在确有需要时才引入,这样就可以减少类的使用者所需引入的头文件数量。否则都是在.h文件中互相引用头文件的话只会一次引入那个头文件中的所有内容,如果次过程持续下去,则会引入许多根本用不到的内容,这当然会增加编译时间。

另外,还有一种特殊情况,就是如果有两个类,它们在各自的头文件中引入对方的头文件,就会导致“循环引用”。当解析其中一个头文件时,编译器会发现它引入了另一个头文件,而那个头文件又回过头来引用第一个头文件。使用#import而非#include指令虽然不会导致死循环,但这却意味着两个类里有一个无法被正确编译。对于此问题大家可以自己编写一个“循环引用”的示例尝试一下。无论是相互持有还是单向循环持有都会无法释放。

什么是循环引用?

当两个不同的对象各有一个强引用指向对方,那么循环引用便产生了,当然多一个对象产生的环也是一样的,无法施放让系统回收。

请添加图片描述

  • 有时候,你写的类需要遵从某个协议,那么该协议必须有完整定义,且不能使用向前声明,因为向前声明只能告诉编译器有某个协议,而此时编译器却要知道该协议中定义的方法。例如下方代码:
//EOCRectangle.h
#import "EOCShape.h"
#import "EOCDrawable.h"

@interface EOCRectangle: EOCShape <EOCDrawable>
@property (nonatomic, assign) float width;
@property (nonatomic, assign) float height;
@end

由于需要遵从协议,所以上方代码中的第二个#import是难免的。鉴于此,最好的方法就是把协议单独放在一个头文件中。要是把EOCDrawable协议放在了某个头文件中,那么只要引入次协议,就必定会引入那个头文件中的全部内容,如此一来,就像上面说的那样,会产生相互依赖问题,而且还会增加编译时间。

所以,每次在头文件引入其他头文件之前,都要先问问自己这样做是否确有必要。如果可以用向前声明取代引入,那就不要引入。若因为要实现属性、实例变量或者要遵循协议而必须引入头文件,则应尽量将其移至 分类 中。这样做不仅可以缩减编译时间,而且还能降低彼此依赖程度。若是依赖关系过于复杂,则会给维护带来麻烦,而且,如果只想把代码的某个部分开放为公共API的话,台复杂的依赖关系也会出问题。

要点总结:

  1. 除非确有必要,否则不要引入头文件。一般来说,应在某个类的头文件中使用向前声明来提及别的类,并在实现文件中引入那些类的头文件。这样做可以尽量降低类之间的耦合。
  2. 有时无法使用向前声明,比如要声明某个类遵循一项协议。这种情况下,尽量把“该类遵循某协议”的这条声明移至 分类 中。如果不行的话,就把协议单独放在一个头文件中,然后将其引入。

多用字面量语法,少用与之等价的方法

字面数值

NSNumber *someNumber = [NSNumber numberWithInt:1];

然而字面量方法如下:

NSNumber *someNumber = @1;

以字面量来表示数值十分有用。这样做可以令NSNumber对象变得整洁,因为声明中只包含数值,而没有多余的语法成分。

字面量数组

NSArray *animals = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];

上面这种做法不仅简单,而且还利于操作数组。数组的创建操作就是取某个下标所对应的对象,这用字面量来做更为容易。如果不用字面量,那么通常会用“objectAtIndex:”方法:

NSString *dog = [animals objectAtIndex:1];

若使用字面量,则是:

NSString *dog = animals[1];

这也叫“取下标”操作,更加简洁更易理解。

但是,如果用字面量方法创建数组的话,如果其中一个元素我们设置其为nil的话,程序在运行时就会报错。

NSArray *animals = @[@"dog", "cat", "mouse"];
NSString *dog = animals[0];

//创建字面量数组
id object1 = @"dog";
id object2 = nil;
id object3 = object1;
NSArray *array1 = [NSArray arrayWithObjects:object1,object2,object3,nil];

NSArray *array2 = @[object1, object2, object3];

以上两种创建方法,第一种不会报错,但是数组array1只有一个元素就是:object1,因为使用“arrayWithObjects”方法时会依次处理各个参数,直到发现nil为止,所以该方法会提前停止;当使用字面量语法创建时,因为object2nil,所以会抛出异常。

这样的好处就是可以及时修改自己在编写代码过程中的问题,如果用标准方法创建数组的话就不会因为元素为nil而报错,导致错误执行的程序难以找到问题之处。

字面量字典

“字典”是一种映射型数据结构,可向其中添加键值对。与数组一样,OC代码也经常用到字典。其创建方式如下:

NSDictionary *personData = [NSDictionarydictionaryWithObjectsAndKeys: @"Matt", @"firstName", @"Galloway", @"lastName", [NSNumber numberWithInt:28], @"age", nil];

这样的写法比较困惑,因为其顺序是<对象>,<键>,<对象>, <键>。这与通常理解的顺序相反,所以不容易读懂。如果改为字面量方法,就清晰多了:

NSDictionary *personData = @{@"firstName" : @"Matt", @"lastName" : @"Galloway", @"age" : @28};

要点总结:

  1. 应该使用字面量语法来创建字符串、数值、数组、字典。与创建此类对象的常规方法相比,这么做更加简明扼要。
  2. 应该通过取下标操作来访问数组下标或字典中的键所对应的元素。
  3. 用字面量语法创建数组或字典时,若值中有nil,则会抛出异常。因此,务必确保值里不含nil
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值