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)。

  1. 第一步是在类的接口部分使用@property指令定义属性
  2. 第二步可选,如果程序需要改变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;
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值