美文网首页
读EffectiveObjective-C2.0(第一条、第二条

读EffectiveObjective-C2.0(第一条、第二条

作者: LazyLoad | 来源:发表于2020-10-15 09:49 被阅读0次

    第一条:了解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协议写在了某个大的头文件中,只要引入协议,还会额外引入大的头文件中的全部内容,这样就会产生相互依赖,而且会增加编译时间。

    • 如果是委托协议,就不用单独写一个文件里了,委托协议只有和接收委托协议的类放在一起才有意义,在类的实现文件中实现委托协议就可以了。

    相关文章

      网友评论

          本文标题:读EffectiveObjective-C2.0(第一条、第二条

          本文链接:https://www.haomeiwen.com/subject/rtjnpktx.html