OC隐藏和封装
Posted Billy Miracle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OC隐藏和封装相关的知识,希望对你有一定的参考价值。
封装
封装是面向对象的三大特征之一(另外两大特征是继承和多态),他指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
对一个类或对象实现良好的封装,可以实现以下目的:
- 隐藏类的实现细节。
- 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问。
- 可进行数据检查,从而有利于保证对象信息的完整性
- 便于修改,提高代码的可维护性。
使用访问控制符
OC提供了4个访问控制符:@private、@package、@protected 和 @public。
- @private(当前类访问权限):该成员变量只能在当前类的内部访问,在类的实现部分定义的成员变量默认为@private。
- @package(相同映像访问权限):该成员变量可以在当前类以及当前类的同一个映像的任意地方访问。
- @protected(子类访问权限):该成员变量可以在当前类、当前类的子类的任意地方访问,在类的接口部分定义的成员变量默认为@protected。
- @public(公共访问权限):使成员变量可以在任意地方访问。
访问控制级别如下表所示:
@private | @package | @protected | @public | |
---|---|---|---|---|
同一个类中 | √ | √ | √ | √ |
同一个映像中 | √ | √ | ||
子类中 | √ | √ | ||
全局范围内 | √ |
示例:
// FKPerson.h
// 疯狂ios@7隐藏与封装@1
#import <Foundation/Foundation.h>
@interface FKPerson : NSObject {
//使用private限制成员变量
@private
NSString* _name;
int _age;
}
-(void) setName: (NSString*) name;//设置_name成员变量的值
-(NSString*) name;//设置_name成员变量的值
-(void) setAge: (int) age;//设置_age成员变量的值
-(int) age;//获取_age成员变量的值
@end
_name 和 _age 两个成员变量位于 @private 之后,只能在当前类中访问。
// FKPerson.m
// 疯狂iOS@7隐藏与封装@1
#import "FKPerson.h"
@implementation FKPerson
-(void) setName:(NSString *)name {
if([name length] >6 || [name length] < 2) {
NSLog(@"您设置的人名不符合要求");
return;
} else {
_name = name;
}
}
- (NSString*) name {
return _name;
}
-(void) setAge:(int)age {
if(_age != age) {
if (age > 100 || age < 0) {
NSLog(@"您设置的年龄不合法");
return;
} else {
_age = age;
}
}
}
- (int) age {
return _age;
}
@end
// main.m
// 疯狂iOS@7隐藏与封装@1
#import "FKPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//因为_age成员变量已隐藏,所以下面的语句编译错误
//如果改为@package就可以操作了
//p->_age = 1000;
FKPerson* p = [[FKPerson alloc] init];
[p setAge: 1000];
NSLog(@"未能设置_age成员变量时:%d", [p age]);
[p setAge: 30];
NSLog(@"成功设置_age成员变量后:%d", [p age]);
[p setName:@"李刚"];
NSLog(@"成功设置_name成员变量后:%@", [p name]);
}
return 0;
}
模块设计讲求高内聚(尽可能把模块的内部数据、功能实现细节隐藏在模块内部独立完成,不允许外部直接干预),低耦合(仅暴露少量的方法给外部使用)。
关于访问控制符的使用,存在如下几条基本原则:
- 类里的绝大部分成员变量都应该使用@provate限制。此外,一些工具方法也应该隐藏在该类的内部,此时应该把这些方法定义在类的实现部分。
- 如果某个类主要作为其他类的父类,则可以考虑使用@protected。
- 希望暴露出来给其他类自由调用的方法应该先在类接口部分定义,然后在类实现部分实现它们。
深入理解@package访问控制符
@package让那些受它控制的成员变量不仅可以在当前类中访问,也可以在同一映像的其他程序中访问。
所谓同一映像,就是编译后生成的同一个框架或同一个执行文件。当编译器最后把@private限制的成员变量所在的类、其他类和函数编译成一个框架库之后,这些类、函数都在同一个映像中,由于其他程序只是依赖这个框架库,其他程序与该框架库就不在同一个映像中。示例:
//FKApple.h
#import <Foundation/Foundation.h>
@interface FKApple : NSObject {
@package
double _weight;
}
@end
//main.m
#import "FKApple.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
FKApple* apple = [[FKApple alloc] init];
apple->_weight = 30;
NSLog(@"APPLE的质量为:%g", apple->_weight);
}
return 0;
}
FKApple类和main()函数位于同一映像中,因此mian()函数可以自由访问FKApple类的_weight成员变量。
合成存取方法
Objective-C 2.0 开始,可自动生成设值方法(setter)和取值方法(getter)。
- 第一步是在类的接口部分使用@property指令定义属性
- 第二步可选,如果程序需要改变getter、setter方法对应的成员变量的变量名,则可以在类的实现部分使用@synthesize指令。
Xcode的模版代码为:@synthesize window = _window,从中可以看出,系统合成的成员变量为property名前加下划线。
如果@synthesize指令后无指定成员变量名,则成员变量名与合成的getter方法同名。
示例:
// Fraction.h
#import <Foundation/Foundation.h>
@interface Fraction : NSObject
@property int numerator, denominator;
-(void) print;
-(void) setTo: (int) n over: (int) d;
-(double) convertToNum;
-(void) add: (Fraction*) f;
-(void) reduce;
@end
// Fraction.m
#import "Fraction.h"
@implementation Fraction
@synthesize numerator;
//@synthesize denominator = _denominator;
//此处省略@synthesize与上面的代码效果相同
//可观察到下面的numerator与_denomiator的区别
//二者一个有下划线,一个没有
-(void) print {
NSLog(@"%i/%i", numerator, _denominator);
}
-(double) convertToNum {
if(_denominator!=0){
return (double) numerator / _denominator;
} else {
return NAN;
}
}
-(void) setTo:(int)n over:(int)d {
numerator = n;
_denominator = d;
}
-(void) add:(Fraction *)f {
numerator = numerator * f.denominator + _denominator * f.numerator;
_denominator = _denominator * f.denominator;
[self reduce];
}
-(void) reduce {
int u = numerator;
int v = _denominator;
int temp;
while (v != 0) {
temp = u % v;
u =v;
v = temp;
}
numerator /= u;
_denominator /= u;
}
@end
// main.m
#import "Fraction.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
id aFraction = [[Fraction alloc] init];
Fraction *bFraction = [[Fraction alloc] init];
[aFraction setTo: 1 over: 4];
//[bFraction setTo: 1 over: 2];
[bFraction setNumerator: 1];
[bFraction setDenominator: 2];
[aFraction print];
NSLog(@"+");
//[bFraction print];
//[bFraction print];
NSLog(@"%i/%i",[bFraction numerator], [bFraction denominator]);
NSLog(@"=");
[aFraction add: bFraction];
//[aFraction reduce];
[aFraction print];
NSLog(@"%lf",[aFraction convertToNum]);
}
return 0;
}
以上代码实现了简单的分数相加,并使用了合成的setter与getter方法。
当使用@property定义property时,还可在@property和类型之间用括号添加一些额外的指示符:
- assign
- atomic(nonatomic)
- copy:调用setter方法对成员变量赋值时,会将被赋值的对象复制一个副本,再将该副本赋给成员变量。当成员变量的类型是可变类型,或其子类是可变类型时,被赋值的对象有可能在赋值之后被修改,此时可以使用copy指示符。
示例:
@property (copy) NSString* name;
此外,getter与setter指示符用于为合成的getter、setter方法指定自定义方法名,例如:
@property (nonatomic, getter = abc, setter = def:) int price;
注意不要忘记setter后的冒号。
点语法
点语法只是一种简化写法,其本质依然是调用getter和setter方法。因此只要该对象有getter方法,程序就可以通过点语法来获取属性值,对象只要有setter方法,程序就可以通过点语法来设置对象的属性值。
示例:
bFraction.numerator = 1;
以上是关于OC隐藏和封装的主要内容,如果未能解决你的问题,请参考以下文章