Objective-C——隐藏和封装

文章目录

  • 封装
    • 封装是什么
    • 为什么要进行封装
    • 访问控制符
    • 举例
  • setter-getter方法
    • setter方法
    • getter方法
    • 举例
    • 读、写属性
  • 点语法
    • 举例
    • 注意点
  • 合成存取方法
    • 举例
  • 补充

封装

封装是什么

它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问

为什么要进行封装

  1. 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,现实对成员变量的不合理访问
  2. 可进行数据检查,从而有利于保证对象信息的完整性
  3. 便于修改,提高代码的可维护性
  4. 隐藏类的实现细节

访问控制符

  1. @private(当前类访问权限) :如果类的成员变量使用@private访问控制符来限制,则这个成员只能在当前类的内部被访问
  2. @package(与映像访问权限相同) :如果类的成员变量使用@package访问控制符来限制,则这个成员可以在当前类以及当前类实现的同一映像的任意地方访问。
  3. @protected(子类访问权限) :如果类的成员变量使用@protected访问控制来限制,则这个成员变量可以在当前类、当前类的子类的任意地方访问
  4. @public(公共访问权限) :这是一个最宽松的访问控制级别,如果类的成员变量使用@public限制,这个成员变量可以在任意地方访问,不管是否处于同一映像中,也不管是否有父子继承关系

注:同一映像就是编译后生成的同一框架或同一个执行文件

举例

设置一个接口部分

#import <Foundation/Foundation.h>

@interface Apple : NSObject
{
	//此时如果不用访问控制符对成员变量进行修饰,那么默认为@protected
    int _appleNumber;       //苹果的数量
}
- (void)eat;
@end

方法实现部分

#import "Apple.h"

@implementation Apple

- (void)eat {
	_appleNumber--;
    NSLog(@"吃了1个苹果,还剩下%d个苹果",_appleNumber);
}
@end

此时如果在main函数里调用apple类接口的成员变量
Objective-C——隐藏和封装_第1张图片
此时编译器会报错,因为在@protected 访问控制符限制下,我们无法直接对成员变量直接进行访问

#import <Foundation/Foundation.h>


@interface Apple : NSObject
{
    @public 		//用@public公开成员变量
    int _appleNumber;       //苹果的数量
}
- (void)eat;
@end

Objective-C——隐藏和封装_第2张图片

可以在成员变量前用 @public 修饰就会消除报错,但这样恰恰违反了封装的本质,我如果在主函数里任意设置_appleNumber变量的数值,甚至将person->appleNumber = -50;这样不符合常理的赋值都能正常运行
Objective-C——隐藏和封装_第3张图片

因此如何对成员变量进行封装是关键:

虽然成员变量不是public之后外界不能直接访问了,但是还是可以在本类的对象方法中直接访问

新提供一个方法addAppleNumber:用于设置成员变量的值,并能对赋的值进行过滤

- (void)addAppleNumber:(int)count
{
    if (count < 0) {
        count = 0;
    }
    _appleNumber = count;
}

原来的- (void) eat;也要稍作变化

- (void)eat {
    if (_appleNumber > 0) {
        _appleNumber--;
        NSLog(@"吃了1个苹果,还剩下%d个苹果",_appleNumber);
    }
    else NSLog(@"没有苹果了");
}

之后如果需要改变成员变量的值只需要在主函数里调用addAppleNumber:方法即可,如下

		//2. 设置苹果的数量
      [person addAppleNumber:50];

由此可见,此处的addAppleNumber:就是对成员变量的封装

规范: 一般情况下不会对外直接暴露成员变量,都会提供一些公有方法进行赋值,将成员变量封装起来

setter-getter方法

  1. 由于将来我们经常需要定义一些方法来操作成员变量,而每个方法都必须有一个有意义的名称,而想名字非常麻烦,所以就有了getter-setter方法
  2. getter-setter方法他的格式和写法都是固定的,所以只要有getter-settter方法就不用费时间去想名字了
  3. 并且getter-setter方法还是程序员之间的一种规范,以后别人只要想给属性赋值立刻就会想到getter-setter方法,这样降低了程序员之间的沟通成本

setter方法

作用:设置成员变量的值

写法:

  1. setter方法一定是对象方法
  2. 一定没有返回值
  3. 一定以set开头,并且set后面跟上需要设置的成员变量的名称去掉下划线,并且首字母大写
  4. 一定有参数,参数类型一定和需要设置的成员变量的类型一致,并且参数的名称就是成员变量的名称去掉下划线

getter方法

作用:获取成员变量的值

写法:

  1. getter方法一定是对象方法
  2. 一定有返回值,而且返回值一定和获取的成员变量的类型相同
  3. 方法名称就是获取的成员变量的名称去掉下划线即可
  4. 一定没有参数

举例

定义一个类接口部分

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
	//	三个属性
    NSString* _name;    //名字
    int _age;          //年龄
    int _weight;       //体重
}
- (void)setName:(NSString*)name;        //  setter方法
- (void)setAge:(int)age;
- (void)setWeight:(int)weight;

- (NSString*)name;                    //   getter方法
- (int)age;
- (int)weight;
@end

类实现部分

#import "Person.h"

@implementation Person
- (void)setName:(NSString*)name         //  setter方法
{
    //成员变量以下划线开头的好处:能区分局部变量和成员变量
    _name = name;
}
- (void)setAge:(int)age
{
    _age = age;
}
- (void)setWeight:(int)weight
{
    _weight = weight;
}

- (NSString*)name                     //  getter方法
{
    return _name;
}
- (int)age
{
    return _age;
}
- (int)weight
{
    return _weight;
}
@end

主函数部分

#import <Foundation/Foundation.h>
#import "Apple.h"
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person* person = [[Person alloc] init];
        [person setName:@"顶梁柱"];
        [person setAge:18];
        [person setWeight:60];
        
        NSLog(@"这个person的名字是%@,今年%i岁,体重为%ikg" , [person name] ,[person age], [person weight]);
    }
    return 0;
}

运行结果如下:

读、写属性

  1. 如果某一个属性只提供了getter方法没有提供setter方法,我们称这个属性为只读属性
  2. 如果某一个属性只提供了setter方法没有提供getter方法,我们称这个属性为只写属性
  3. 如果某一个属性同时提供了setter和getter方法,我们称这个属性为可读可写属性
  4. 如果某一个属性没有提供setter和getter方法,我们称这个属性为私有属性

点语法

如果给属性提供了getter、setter方法,那么访问属性就又多了一种方式,点语法

  1. 点语法的本质就是调用了我们的getter、setter方法
  2. 如果点语法在=号的左边,那么编译器会自动转换成setter方法
  3. 如果点语法在=号的右边,或者没有等号,那么编译器就会自动转换成getter方法

举例

将刚才的代码换成点语法,如下

#import <Foundation/Foundation.h>
#import "Apple.h"
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person* person = [[Person alloc] init];
//        [person setName:@"顶梁柱"];
//        [person setAge:18];
//        [person setWeight:60];
        person.name = @"顶梁柱";
        person.age = 18;
        person.weight = 60;
//        NSLog(@"这个person的名字是%@,今年%i岁,体重为%ikg" , [person name] ,[person age], [person weight]);
        NSLog(@"这个person的名字是%@,今年%i岁,体重为%ikg" , person.name ,person.age, person.age);
    }
    return 0;
}

注意点

点语法一般用于给成员变量赋值,如果不是给成员变量赋值的情况下不建议使用

合成存取方法

前面介绍了为成员变量自己实现setter方法和getter方法,这种做法虽然并不难,但如果一个类中包含10个甚至更多的成员变量时,为每个成员变量都编写setter、getter方法将是一件令人乏味的事情
因此从Objective-C 2.0版本开始,它自动合成了setter、getter方法,这样开发人员就可以避免书写乏味的setter、getter方法了。

让系统自动合成setter和getter方法只要如下两步。

  1. 在类接口部分使用@property指令定义属性。使用@property定义属性时无须放在接口部分的花括号里,而是直接放在@interface、@end之间定义。@property指示符放在属性定义的最前面
  2. 在类实现部分使用@synthesize指令声明该属性即可

举例

接口部分

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property NSString* name;
@property int age;
@property int weight;
@end

实现部分

#import "Person.h"

@implementation Person
@synthesize name;
@synthesize age;
@synthesize weight;
@end

主函数与刚才相同,使用点语法进行访问或者赋值,和刚才的运行结果相同

补充

Objective-C——隐藏和封装_第4张图片

你可能感兴趣的:(objective-c,封装)