#import @class 的区别

关键词:#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.协议:后续文章讲这个
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容