您的位置:首页 > 移动开发 > Objective-C

【IOS 开发学习总结-OC-11】★objective-c面向对象之——封装和访问控制符

2015-09-21 08:47 603 查看

封装

封装的定义

面向对象的三大特征(封装,继承,多态)之一。指的是:将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

良好的封装,可以实现的目的

对一个类或对象实现良好的封装,可以实现的目的有:

1. 隐藏类的实现细节;

2. 使使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问。

3. 可进行数据检查,有利于保证对象信息的完整性;

4. 便于修改。提高代码可维护性。

如何实现良好的封装

把对象的成员变量和实现细节隐藏起来,不允许外部直接访问。

把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。

访问控制符

封装需要使用 objective-c 提供的4个访问控制符:@private,@package,@protected和@public。它们分别代表了4个访问控制级别。

它们的访问控制级别由小到大依次如下图:



4个访问控制符说明如下:

1. @private:【当前类访问权限】,只能在当前类内部被访问。用于彻底隐藏成员变量。类的实现部分定义的成员变量相当于默认用这种访问权限。

2. @package:【与映像访问权限相同】用于部分隐藏成员变量。如果类的成员变量使用@package 访问控制符来限制,则这个成员可以在当前类及当前类实现的同一个映像的任意地方访问。

3. @protected:【子类访问权限】用于部分隐藏成员变量。如果类的成员变量使用@protected 访问控制符来限制,则这个成员可以在当前类,当前类的子类的任意地方访问。在类的接口部分定义的成员变量默认使用这种访问权限。

4. @public:【公共访问权限】,用它限制的类的成员可以在任意地方访问,不管是否处于同一映像中,也不管是否具有继承关系。

综上,我们用图表进行下总结:




那么,局部变量能否被访问控制符修饰呢?

答案是不能。因为对于局部变量而言,它的作用域是它所在的方法,不可能被其他类访问。所以不能用访问控制符来修饰。而访问控制符用于控制类的成员变量是否可以被其他类访问。

示例程序:

FKPerson.h文件

#import <Foundation/Foundation.h>

@interface FKPerson : NSObject
{
    // 使用@private限制成员变量
    @private
    NSString* _name;
    int _age;
}
//提供方法来操作name Field
- (void) setName: (NSString*) name;
// 提供方法来获取_name成员变量的值
- (NSString*) name;
// 提供方法来设置age成员变量
- (void) setAge:(int) age;
// 提供方法来获取_age成员变量的值
- (int) age;
@end


FKPerson.m文件

#import "FKPerson.h"

@implementation FKPerson
// 提供方法来设置_name成员变量
- (void) setName: (NSString*) name
{
    //执行合理性校验,要求用户名必须在2~6位之间
    if ([name length] > 6 || [name length] < 2)
    {
        NSLog(@"您设置的人名不符合要求");
        return;
    }
    else
    {
        _name = name;
    }
}
// 提供方法来获取_name成员变量的值
- (NSString*) name
{
    return _name;
}
// 提供方法来设置age成员变量
- (void) setAge:(int) age
{
    if(_age != age)
    {
        //执行合理性校验,要求用户年龄必须在0~100之间
        if (age > 100 || age < 0)
        {
            NSLog(@"您设置的年龄不合法");
            return;
        }
        else
        {
            _age = age;
        }
    }
}
// 提供方法来获取_age成员变量的值
- (int) age
{
    return _age;
}
@end


【知识切入】访问权限控制符的作用范围是如何划分的呢?

@private,@package,@protected和@public,这4个权限控制符的作用范围是这样的,从它们出现的位置开始,到下一个权限控制符出现或右边花括号之间的成员变量,都受到该访问权限控制符的控制。

oc 中成员变量的 setter 和 getter 方法有非常重要的意义。如,某个类中包含了一个名为_sun的 Field, 则其对应的 setter 和 getter 方法名应为 setSun 和 sun(setter 方法名为成员变量名首字母大写,并在前面分别增加 set 动词,getter 方法名为去掉画线前缀)

示例程序:

#import "FKPerson.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        FKPerson* p = [[FKPerson alloc] init];
        // 因为age成员变量已被隐藏,所以下面语句将出现编译错误。
//      p->_age = 1000;
        // 下面语句编译不会出现错误,但运行时将提示出入的age成员变量不合法
        // 程序不会修改p的age成员变量
        [p setAge: 1000];
        // 访问p的age成员变量也必须通过其对应的getter方法
        // 因为上面从未成功设置p的age成员变量,故此处输出0
        NSLog(@"未能设置age成员变量时:%d" , [p age]);
        // 成功修改p的age成员变量
        [p setAge:30];
        //因为上面成功设置了p的age成员变量,故此处输出30
        NSLog(@"成功设置age成员变量后:%d" , [p age]);
        // 不能直接操作p的name成员变量,只能通过其对应的setter方法
        // 因为"李刚"字符串长度满足2~6,所以可以成功设置
        [p setName:@"李刚"];
        NSLog(@"成功设置name成员变量后:%@" , [p name]);
    }
}


这里的 FKperson 对象已经不能再直接修改它的成员变量。只能通过各自对应的 setter,getter 方法进行设置成员变量的值。当 setter 方法设置响应的成员变量的时,允许程序员在 setter 方法中增加自己的控制逻辑,保证这些成员变量不会出现与实际不符的情况。

访问控制符使用的基本原则

类里绝大部分成员变量都应该使用@private 限制。

工具方法(指只是辅助实现该类的其他方法的方法)应该隐藏在该类的内部,此时应把这些方法定义爱类实现部分。

如果某个类主要作为其他类的父类,该类里包含成员变量希望被子类访问,则可以考虑用@protected 限制这些成员变量。

暴露出来给其他类自由调用的方法,应该先在类接口部分定义,然后在类实现部分实现它们。

理解@package

受@package限制的成员变量,步进可以在当前类中访问,也可以在相同映像的其他程序中访问。那么:什么是”同一映像”呢?

同一映像,简单的说,就是编译后生成的同一个框架或者同一个执行文件。

比如说,我们想开发一个基础框架,若用@private 限制成员变量,则限制太死——考虑该框架中的其他类,其他函数可能需要直接访问该成员变量,但该框架又不希望其他外部程序访问该成员变量。此时考虑用@package 来限制成员变量。

当编译器把@package限制的成员变量所在的类,其他类和函数编译成一个框架库之后,那么这些类,函数都在同一个映像中,此时这些类,函数都可以自由访问由@ package 限制的成员变量。但是,其他程序引用这个框架库时,由于其他程序只是依赖这个框架库,其他程序与该框架库就不在同一映像中,因此,其他程序无法访问这个@ package 限制的成员变量。

示例程序:

FKApple.h文件

#import <Foundation/Foundation.h>

@interface FKApple : NSObject
{
    // 使用@package限制成员变量
    @package
    double _weight;
}
@end


FKApple.m 文件

#import "FKApple.h"

@implementation FKApple
@end


FKAppleTest.m文件

#import "FKApple.h"

int main(int argc , char * argv[])
{
    @autoreleasepool{
        FKApple* apple = [[FKApple alloc] init];
        // 下面程序程序直接访问@package限制的成员变量,最终赋值成功
        apple->_weight = 30.4;
        NSLog(@"apple的重量为:%g" , apple->_weight);
    }
}


运行编译后,编译器会把FKApple.h,FKApple.m,FKAppleTest.m文件编译成一个执行文件,由于FKApple类和 main() 函数同在一个映像中,因此main() 函数可以自由访问FKApple类中被@package 限制的成员变量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: