interface和setter,getter

前篇说到我们通过ObjC的Category特性给日常工作增加便捷的实现,这一篇则要从语言设计角度,跟大家分享一些思考。

不要忽视interface

ObjC的@interface设计,跟Java和C#真的很像,但又略有不同,相比之下Java和C#则像是一个模子刻出来的。ObjC的特点十分明显,首先是一般不用写@private@public来区分私有变量,大部分ObjC开发者甚至都不知道还有这两个关键字,其实Cocoa源代码中也基本没有使用过这种设计,即使ObjC是支持的。

在@interface 中使用 @private和@public

@interface Student : NSObject
{
    @private
    NSString* _name;
    @public
    NSNumber* _age;
    int _height;
}
@end

如上代码中,Student有一个私有变量_name,和两个共有变量_age_height,但在@interface中声明变量,一定不是Cocoa设计者的初衷,这里有两个方面的考虑。

其一,把内部变量直接暴露在外,会降低整个框架的稳定性,因为增加不同模块之间的耦合,降低了每个类的内聚性。
其二,内部变量的变量名,很容易跟局部变量变量名产生冲突。上例中我给每一个变量名前加了下划线,就是为了防止这个问题发生。

所以纵观Cocoa框架的头文件设计,基本没有这样的代码,因为设计者提供了更好的实现方式,就是大家用的更多的@property关键字。

如果用@property声明上面的类,大家都很熟悉

@interface Student : NSObject

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSNumber* age;
@property (nonatomic, assign) NSInteger height;

@end

@property这个设计真的很有意思,首先我们不再区分私有公有属性,因为只要写在.h里面的@property,我们都默认是共有的,私有的@property可以写在.m文件里。

其次,配合写在@implementation里面的@synthesize关键字,可以自动生成setter和getter方法,而现在@synthesize关键字都可以省略,除了个别情况有修改内部变量名称的需求。

@implementation Student
@synthesize name = _name;
@synthesize age = __age;
@end

上面的@synthesize,第一个是可以省略的,在不写的情况下,编译预处理会自动给添加@synthesize代码,所以即使没有合成(synthesize)height属性,我们依然实现了它的setter和getter方法

//这两个方法可以重写

- (void)setHeight:(NSInteger)height
{
    _height = height;
}

- (NSInteger)height
{
    return _height;
}

在setter和getter方法均重写的情况下,@synthesize需要手动添加。

@synthesize height = _height;

为什么要使用 setter 和 getter

settergetter的设计的确值得琢磨,我们主要从以下几点分析:

setter 和 getter 包装了内部变量,整个类对外可以只暴露接口,增强类的内聚性。

例如上例中的内部变量_name,外部类是无法操作的,只能通过set和get接口来发消息:

Student* s = [[Student alloc] init];

[s setName:@"Tom"];
[s name];

通过实现 getter方法,可以避开初始化变量的时机问题

这也是很实用的一个点,因为ObjC的消息设计机制,导致ObjC很难在初始化(init)方法中传入过多参数(题外话,我给ObjC扩展过依赖注入,详见iOS实现依赖注入)。因此新实例的默认属性,放在什么位置实现合适,是大家一定遇到过的问题。

例如最常见的UIViewController,代码初始化走init方法,而通过storyboard实力化则走initWithCoder方法,一些容器属性,通过getter方法初始化,则可避免第一次调用尚未初始化造成的问题。

早期的Cocoa在如果给nil发消息,是会引起异常的,现在的版本给没有alloc的对象发消息不再抛异常,以至于某些时候属性没有初始化造成的问题变得更隐蔽,然而重写getter方法可以有效避免这个问题,例如:

//班级类
@interface XXClass : NSObject
@property (nonatomic, strong) NSMutableArray* students; //学生
@end

@implementation

//实现getter方法,在内部变量_students没初始化的情况下将其初始化
- (NSMutableArray *)students
{
    if (!_students) {
        _students = [NSMutableArray array];
    }
    return _students;
}

@end

如此一来,无论在任何时候,第一次发送[self students]消息的时候,内部变量_students都会初始化。

在这里要另外注明一点,在类的内部,不要在setter和getter方法外,直接使用内部变量,遵守这一条会收益很多。

setter 和 getter 可以单独使用,也可以脱离内部变量使用

这里要说的就是@property的灵活性了,大家知道@property拥有一系列的修饰词,除了常用的nonatomic(非原子化,线程安全)strong(强引用类型)weak(弱引用类型)assign(赋值,用于非对象属性)以外,还有readonly(只读)readwrite(可读写)两个影响setter和getter方法的属性,readonly修饰的属性,只有getter方法而没有setter方法。

readwrite则是一个看起来可有可无的修饰词,因为默认就是可读写。然而它其实有个专门设计的用法,就是在.h中的interface中被readonly修饰的属性,可以在这个类的其他类别(category)或者匿名类别中重新声明这个属性时,修改其读写限制,例如

//班级类
@interface XXClass : NSObject
@property (nonatomic, strong, readonly) NSMutableArray* students; //学生
@end
//匿名类别
@interface XXClass()
@property (nonatomic, strong, readwrite) NSMutableArray* students;
@end

这样一来,因为匿名类别一般写在.m文件里(基本没见过写在.h文件里的),所以外部是不能调用students属性的setter方法,而XXClass类内部则可以使用。

还有一种常见情况是用setter和getter来模拟属性(@property),例如:

//班级类
@interface XXClass : NSObject
@property (nonatomic, strong, readonly) NSMutableArray* students; //学生
@property (nonatomic, assign, readonly) NSUInteger studentsCount; //学生数量
@end
- (NSUInteger)studentsCount
{
    return self.students.count;
}

这里的studentsCount是没有内部变量的,通过getter方法伪造成属性接口。

小结

这一篇是ObjC的接口设计模式的一部分,写的比较详细是帮助新手入门,给有经验的朋友带来一些思考,并引出接下来的内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 中国美业百家名师 2016美甲世界杯裁判员 美业分会技术美甲导师 广东省人力资源局高级美甲师 深圳市职业培训学校职...
    美业纪录阅读 460评论 0 0
  • 一直以来,我对自己的睡眠都是特别的满意,因为我是属于躺下就能睡着类型的,一般情况下都能在闹钟响前几分钟醒来。不过起...
    小粉猪阅读 591评论 1 1
  • 林庚——春天的心如草的荒芜 随便的踏出门去 美丽的东西到处可以拣起来 少女的心情是不能说的 天上的雨点常是落下 而...
    遇了个黑天鹅阅读 376评论 0 0