[Objective-C]爷来力-面向对象之封装与其中的合成存取方法

[Objective-C]爷来力-面向对象之封装与其中的合成存取方法

良好封装

面向对象之封装

Objective-C 同样支持面向对象的三大基本特征:封装,继承,多态
我们今天先介绍其中的第一个特征:封装
封装(Encapsulation)指的是将对象的状态信息隐藏在对象内部不允许外部程序直接访问对象内部信息,而通过该类所提供的方法来实现对内部信息的操作和访问

什么是良好封装

对一个类或对象实现良好的封装需要实现以下目的:

  1. 隐藏类的实现细节
  2. 让使用者只能通过预先设定好的方法对数据进行访问,限制对成员变量的不合理访问。
  3. 将对象的成员变量以及实现细节隐藏起来,不允许外部直接访问。
  4. 暴露方法,让方法来控制对这些成员变量进行安全的访问与操作。

那么,实现上述操作以达到良好封装又有什么益处呢

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

来看一个良好封装

#import<Foundation/Foundation.h>

@interface ECPreson: NSObject
{
	@private
	NSString* _name;
	int _age;
	//这两个成员变量都位于 @private 之后,表明这两个成员变量都只能在当前类中访问
}
- (void)setName: (NSString*)name;
- (void)setAge: (int)age;
- (NSString*)name;
- (int)age;
@end

如何访问

要实现上述的隐藏与暴露操作,需要通过 Objective-C 提供访问控制符来实现

四种访问控制符

OC中有如下四种访问控制符:

  1. @private(当前类访问权限):

这个成员只能在当前类的内部被访问
用于彻底隐藏成员变量

在类的实现部分定义的成员变量相当于默认使用这种权限

  1. @package(与映像访问权限相同):

这个成员可以在当前类以及当前实现的同一映像的任意地方访问
用来隐藏部分成员变量

  1. @protected(子类访问权限):

这个成员可以在当前类当前类的子类的任意地方访问
用来暴露部分成员变量

  1. @public(公共访问权限):

这个成员变量可以在任意地方进行访问
最宽松的访问控制符

即下图
请添加图片描述

  1. 符合规范的类:如果成员变量被private限制,并且每个成员变量提供了getter与setter的方法。那么该类就是一个符合规范的类

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

  3. 使用范围:从访问控制符出现的位置开始,到下一个访问控制符或右花括号之间的成员变量,都受该访问控制符的控制

理解 @package

@public 访问控制符则用于彻底暴露受它控制的成员变量
@protected 则让那些受它控制的成员变量不仅可以在当前类中访问,也可以在其子类中访问
@package 让那些受它控制的成员变量不仅可以在当前类中访问,也可以在同一映像的其他程 序中访问
那么,我们详细看一看什么是同一映像吧

简单地说,就是编译后生成的同一个框架或同一个执行文件
比如,想开发一个基础框架,如果使用 @private 限制某个成员变量,则限制得太死了——考虑该框架中其他类、其他函数可能也需要直接访问该成员变量,但该框架又不希望其他外部程序访问该成员变量,此时就可以考虑使用 @package 来限制该成员变量。当编译器最后把 @private 限制的成员变量所在的类、其他类和函数编译成一个框架库之后,这些类、函数都在同一个映像中, 此时这些类、函数都可以自由访问这个 @package 限制的成员变量。但其他程序引用这个框架库时,由于其他程序只是依赖这个框架库,其他程序与该框架库就不在同一个映像中 ,因此, 其他程序无法访问这个@package 限制的成员变量。

来看个栗子

接口部分

#inport<Foundation/Foundation.h>

@interface ECApple: NSObject {
	@package
	double _weight;
}
@end

不写实现了,直接看main

#improt"ECApple"

int main(int argv, char* agrc[]) {
	@autoreleasepool {
		ECApple* apple = [[ECApple alloc]init];
		apple->_weight = 30.4;
		NSLog(@"appleis:%g", apple->_weight);
		//由于 ECApple 类和 main 函数位于同一个映像中,因此 main 函数可以自由访问 ECApple 类的 _weight 成员变量
	}
}

合成存取

setter 与 getter

在 Objective-C 中成员变量的 setter 与 getter 方法十分重要;
由于用 @private 表明这个成员变量只能在当前类中访问
因此我们需要设置一个 getter 方法来获取成员变量而不直接调用
setter 方法的使用同理

对与 setter 与 getter 方法的名字:

例如我们某个类中有个叫 abc 的成员变量,

setter方法的名字
成员变量名的首字母大写前面加上 set
写作 setAbc

getter 方法的名字
去除下划线
写作 abc

看看上文良好封装的实现部分

#import"ECPreson"

@implementation ECPreson
- (void)setName: (NSString*)name {
	if ([name length] > 6 || [name length] < 2) {
		NSLog(@"0");
		retunn;
	} else {
		_name = name;
	}
}
- (NSString*) name {
	return _name;
}
- (void)setAge: (int)age {
	if(_age != age) {
		if(age > 100 || age< 0) {
			NSLog(@"0");
		} esle {
			_age = age;
		}
	}
}
- (int)age {
	return _age;
}
@end

合成存取的方法

上面的代码写了 setter 和 getter 方法
虽然简单,但是在成员变量很多的时候就显得有些不方便
Objective-C 可以自动合成以上两个方法,下面是几个步骤

  1. 在类接口部分使用@property指令定义我们的属性,我们将其放在我们的interface与end的中间
    @property 指令放在属性定义 的最前面
    2.如果需要改变getter、setter 方法对应的成员变量的变量名
    在类实现部分使用@synthesize声明该属性即可

使用上述两个步骤合成存取方法后,不仅会合成成对的方法,还会在类实现部分定义一个与 getter 方法同名的成员变量
如果为某个类定义了一个成员变量,并提供了相应的 getter 与 setter 方法,那么可称定义了一个属性

@property 的语法结构
@property 数据类型 变量名;
property 即属性的意思

@synthesize 的语法结构
@synthesize property 名 [= 成员变量名];
synthesize 即合成的意思

在上面的语法格式中,如果@synthesize 指令后没有指定成员变量名,那么成员变量名默认与合成的getter 方法同名。
如果希望 @property 合成的属性对应的成员变量名保持为 getter 方法名加下画线前缀,则可以省略 @synthesize 定义

来看看代码吧

接口部分

#import<Foundation/Foundation.h>

@implementation ECUser: NSObject
@property (nonatomic) NSString* name;
@property NSString* pass;
@property NSDate* birth;
@end

实现部分

#import"ECUser"

@implementation ECUser
@synthesize name = _name;
//指定name property底层对应的成员变量名为_name,这行代码可以省略
@synthesize pass;
@synthesize birth;
- (void)setName: (NSString*)name {
	self->_name = [NSString stringWithFormat: @"+++%@", name];
}
@end

四个额外指示符

当使用 @property 定义 property 时,还可在 @property 和类型之间用括号添加一些额外的指示符,可使用的特殊指示符如下

assign :

该指示符指定对属性只是进行简单赋值,不更改所赋的值的引用计数

atomic(nonatomic):

指定合成的存取方法是否为原子操作。所谓原子操作,主要指是否线程安全。如使用,则是安全的

copy:

如果使用copy指示符,当调用setter方法对成员变量赋值时,会将被赋值的对象复制一个副本,再将该副本赋值给成员变量。copy指示符会将原成员变量所引用对象的引用计数减一。

retain :

当把某个对象赋值给该属性时,该属性原来所引用的对象的引用计数减1,被赋值的对象引用计数加1

  • 当一个对象的引用计数大于0时,表明该对象还不应该被回收
  • 引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法

本篇小结

  1. 面向对象的三大基本特征是封装,继承,多态
  2. 存在 @private @package @protected @public 四个控制运算符,来控制对成员变量的访问
  3. 可以通过 @property @synthesize 来实现合成存取
  4. 当使用 @property 定义 property 时,还可在 @property 和类型之间用括号添加一些额外的指示符

另1 点语法与箭头->

找到一篇写的很详细的博客 大家可以看看
https://www.cnblogs.com/myqiqiang/p/3888843.html

总而言之,

. (点语法)是访问类的属性,本质是调用 setter、getter 方法

-> 是访问成员变量,但成员变量默认受保护,所以常常报错,手动设为public即可解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值