美文网首页关键字
#import @class 的区别

#import @class 的区别

作者: Kailee | 来源:发表于2017-04-16 21:10 被阅读40次

    关键词:#import、#class、class-continuation 分类、点语法(直接访问还是通过属性访问)、协议等

    文章是参考书籍与博客的总结,自己写下来也算是自我总结,加深印象~

    一、前言

    在 OOP 编程中有两个技术用于描述类或对象与对象之间的关系:一个是继承;另一个是复合。在 Object-C 中 ,当一个类需要引用另一个类,即建立复合关系时,需要在类的头文件 (h) 中,通过 #import 修饰符来建立被引用的指针。

    通常情况下我们都使用 "#import" 修饰符引用类,但从代码质量与安全角度来看,使用 "#import" 建立复合关系时,也暴露了所引用类的实体变量与方法。很多时候我们并不需要知道关于这个类的更多信息,那么只需要了解它是通过指针引用即可,同时也减少了依赖关系,也减少了重新编译所产生的影响。

    二者在编译效率上也存在很大差异。对于已经产生的引用,若被引用的头文件有变化时,那么引用它的类都需要重新编译,这将耗费大量的时间,而使用 @class 则不会。

    对于初学者,使用 #import 还容易犯 "类循环依赖" 错误。即两个类互相引用对方,使用 @class 互相引用虽然不会出现编译错误,但还是尽量避免这种 "类循环依赖" ,因为容易造成高耦合多依赖,不便于维护。

    二、何时使用 #import 何时使用 @class

    既然 #import 有很多不足之处,但是很多情况下不得不用 #import,如在一个头文件 (.h) 中包含多个类的声明定义时,要与该头文件声明的多个类建立复合关系,即所引用的类所处的文件有多个类或者多个其他的定义,使用 #import 比较好。如下:

    #import <Foundation/Foundation.h>
    
    //B 类
    @interface BClass : NSObject
    
    @end
    
    //C 类
    @interface CClass : NSObject
    
    @end
    
    //D 类
    @interface DClass : NSObject
    
    @end
    

    一般来说,使用 @class 只是为了在头文件中引用这个类,把之当做类型来用。同时,在实现类 .m 中 ,如果需要引用这个类的实体变量或方法等,还需要通过 #import 把在 @class 中声明的类引用进来。下面举例说明一下:

    #import <Foundation/Foundation.h>
    
    @interface ClassA : NSObject
    
    @property (nonatomic, copy) NSString *title;
    
    @end
    
    @import UIKit.UIViewController;
    
    @class ClassA;
    @interface ViewController : UIViewController
    
    @property (nonatomic, strong) ClassA *aClass;
    
    @end
    
    #import "ViewController.h"
    #import "ClassA.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        NSLog(@"%@", self.aClass.title);
        
    }
    
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (ClassA *)aClass {
        if (!_aClass) {
            _aClass = [[ClassA alloc] init];
        }
        return _aClass;
    }
    
    @end
    
    四、class-continuation 分类

    综上,在设计类的时候,要分清何时使用 #import 何时使用 @class,是否有必要引入头文件等。若因为要实现属性、实例变量或者要遵循协议而必须引入头文件,则应尽量将其移至“class-continuation 分类”中,这样不仅可以缩减编译时间,而且还能降低彼此依赖程度。下面就讲讲 “class-continuation 分类”。

    类中经常会包含一些无需对外公布的方法以及实例变量(当然也可以对外公布,并写明其为私有)。然后,我们最好还是只把确实需要对外公布的部分公开。那么不公开的怎么写呢,其实就是用“class-continuation 分类”。

    “class-continuation 分类” 和普通分类不同,它必须定义在其类的实现文件中,与其他分类不同,“class-continuation 分类”没有名字。比如有个类叫 KPerson,其“class-continuation 分类” 写法如下:

    @interface KPerson()
    
    @end
    

    看到这里,可能突然明白,哦,原来这就是“class-continuation 分类”~
    我们平时也一直这么用...

    @interface ViewController ()
    
    @end
    

    在分类其中可以定义方法和实例变量,放在里面的相当于隐藏起来,仅供本类使用。
    当然的确可以写在头文件中,哪怕是将其标注为 private,也还是会泄露实现细节。别人知道有个视图宽度的属性了。或者你可以将这个值改为强类型 id,但是 id 类型不够友好,在实现类中无法获得编译器的帮助,即辅助检查功能,且自己也难以理解。

    #import <Foundation/Foundation.h>
    
    @interface ClassA : NSObject{
    @private
        NSInteger viewWidth;
    }
    @end
    

    编写 C++ 代码时“class-continuation 分类” 尤为有用。由于游戏大多用 C++ 来写,若把 C++ 文件的引用放在头文件中(实现文件必须是 .mm 扩展名表示编译器应该将此文件按 Object-C++ 编译)。后果就是所有引入了这个引入了 C++ 文件的类,都要改为 .mm,这可能导致整个应用程序都改为 .mm 了(互相引用是有可能的)。使用“class-continuation 分类”即将 C++ 的引入放到了其实现文件中,头文件没有 C++ 代码了,使用这个头文件的人甚至不知道其内部混有 C++ 代码,对外暴露的是一套纯 Object-C 接口。

    “class-continuation 分类”还有一种合理用法,就是将 public 接口中标为只读的属性扩展成可读写,以便在内部设置其值。通过触发键值观测通知,其他对象有可能正监听此事件。只需要像下面的几行代码就行。实现类既可以随意调用 setFirstName: 或 setLastName: 这两个设置方法,也可以用点语法来设置属性,这样做既可以让外界无法修改对象,又能在其内部按照需要管理其数据。

    .h 文件
    #import <Foundation/Foundation.h>
    
    @interface ClassA : NSObject
    
    @property (nonatomic, copy, readonly) NSString *firstName;
    
    @property (nonatomic, copy, readonly) NSString *lastName;
    
    @end
    .m文件
    #import "ClassA.h"
    
    @interface ClassA()
    
    @property (nonatomic, copy, readwrite) NSString *firstName;
    
    @property (nonatomic, copy, readwrite) NSString *lastName;
    
    @end
    
    @implementation ClassA
    
    @end
    
    五、关于降低类与类的耦合度

    在我看来,提高效率倒是其次,毕竟硬件发展这么快,这种优化提升不大(话说回来,精益求精还是需要的)。其实不管采取哪种方式都可以,首要目的还是为了降低耦合度,降低文件代码之间的粘合性,依赖关系过于复杂会失去代码重用性,给维护增加难度。

    如果建立的复合关系过于复杂时(无论使用 #import 或 @class),这时可以考虑 2 种方式来解决:

    1.模块方式(这个我也不是很明白,有待了解)
    2.协议:后续文章讲这个

    相关文章

      网友评论

        本文标题:#import @class 的区别

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