读EffectiveObjective-C2.0(第一条、第二条
2020-10-15 本文已影响0人
LazyLoad
第一条:了解Objective-C语言的起源
- Objective-C使用消息结构。消息结构的语言,其运行时所应执行的代码由运行环境决定;而使用函数调用的语言,则由编译器决定。Objective-C是一门动态的语言。
- Objective-C对象的内存管理使用的是引用计数机制。
第二条:在类的头文件中尽量少引用其他头文件
- 用Objective-C语言编写类的标准方式为:以类作为文件名,分别创建两个文件,头文件用后缀.h,实现文件用后缀.m。
// EOCPerson.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end
NS_ASSUME_NONNULL_END
// EOCPerson.m
#import "EOCPerson.h"
@implementation EOCPerson
@end
- 假设,过段时间,我们可能需要一个新的类
EOCEmployer
,并且每个EOCPerson
实例都会有一个EOCEmployer
属性。此时我们需要在EOCPerson
的头文件中,定义一个EOCEmployer
属性。
// EOCPerson.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end
NS_ASSUME_NONNULL_END
- 此时,编译器会报错,原因是找不到
EOCEmployer
类,那么我们可以通过引入EOCEmployer
的头文件来解决这个报错。
#import "EOCEmployer.h"
- 这种办法成功解决了编译器的报错,但是不够优雅,原因是:在编译
EOCPerson
类的时候,不需要知道EOCEmployer
的全部细节,只需要知道有一个类名叫EOCEmployer
就好了。 - 我们可以使用向前声明,告诉编译器有
EOCEmployer
这个类,而不需要去引入EOCEmployer
的全部内容。格式为:@class 类名;
// EOCPerson.h
#import <Foundation/Foundation.h>
@class EOCEmployer;
NS_ASSUME_NONNULL_BEGIN
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end
NS_ASSUME_NONNULL_END
- 那么,在什么时候才需要真正引入
EOCEmployer
的头文件呢?在EOCPerson
的实现文件中,真正需要使用EOCEmployer
的时候。我们就必须要知道EOCEmployer
的全部细节了。
// EOCPerson.m
#import "EOCPerson.h"
#import "EOCEmployer.h"
@implementation EOCPerson
- (void)setEmployer:(EOCEmployer *)employer {
_employer = employer;
_firstName = employer.nickName;
}
@end
将引入头文件的时机尽量延后,在确实需要使用的时候引入,这样可以减少类的使用者所需引入头文件的数量。假设把EOCEmployer.h
的头文件引入到EOCPerson.h
,那么其他文件引入EOCPerson.h
就会一并引入EOCEmployer.h
的全部内容,而其他文件不需要用到EOCEmployer.h
中的内容,这么延续下去的话,会引入许多额外的没有用的头文件,就会增加编译时间。
-
向前声明还可以解决两个类相互引用的问题。假设为
EOCEmployer
类加入新增及删除雇员的方法。此时需要我们引入EOCPerson
类,否则找不到EOCPerson
类。
- (void)addEmployee:(EOCPerson *)person;
- (void)removeEmployee:(EOCPerson *)person;
-
如果我们在
EOCPerson
的头文件中引入EOCEmployer
的头文件,并且在EOCEmployer
头文件中引入EOCPerson
的头文件,此时就会产生循环引用的问题。此时编译器会报错。使用向前声明即可解决这样的问题。 -
有些时候必须在头文件中引入其他头文件。不能使用向前声明。
- 如果你写的类,继承自某个父类,则必须引入父类的头文件
// EOCRectangle.h #import "EOCShape.h" NS_ASSUME_NONNULL_BEGIN @interface EOCRectangle : EOCShape @property (nonatomic, assign) CGFloat width; @property (nonatomic, assign) CGFloat height; @end NS_ASSUME_NONNULL_END
- 如果你写的类,遵守某个协议,那么该协议必须有完整的定义,不能使用向前声明。
#import "EOCShape.h" #import "EOCDrawable.h" NS_ASSUME_NONNULL_BEGIN @interface EOCRectangle : EOCShape <EOCDrawable> @property (nonatomic, assign) CGFloat width; @property (nonatomic, assign) CGFloat height; @end NS_ASSUME_NONNULL_END
-
协议最好放在一个单独文件里进行定义,如果把
EOCDrawable
协议写在了某个大的头文件中,只要引入协议,还会额外引入大的头文件中的全部内容,这样就会产生相互依赖,而且会增加编译时间。 -
如果是委托协议,就不用单独写一个文件里了,委托协议只有和接收委托协议的类放在一起才有意义,在类的实现文件中实现委托协议就可以了。