【OC】类与对象

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

类是面向对象的重要内容,我们可以把类当成一种自定义的数据类型,可以使用类来定义变量,这种类型的变量相当于指针类型的变量。也就是说,所有的类都是指针类型的变量

一、定义类

面向对象程序设计中有两个很重要的概念:类与对象(也叫做实例)。类是对一批对象的抽象。类实际上是某种概念,人才是实体。例如我们日常接触到的同学, 实际上都是实例,可以理解成人这个集合当中的某个元素

同时OC定义类分为两部分
接口部分:定义类所包含的成员变量以及方法
实现部分:为该类的方法提供实现

1.接口部分

在这里插入图片描述
其中interface的意思是接口,@interface用于声明定义类的接口部分。@end表示结束。
后面的花括号用于声明成员变量。花括号后面的部分用于声明该类的方法
成员变量:用于描述该类对象的状态数据,例如一个人的身高体重
方法:用于描述该类的行为,
同时接口声明放在h文件中

2.定义成员变量

与C语言类似,但是要以下划线"_" 开头。
同时成员变量应该以一个或多个有意义的单词连缀,一般遵循第一个单词首字母小写,后面每个单词首字母大写,其余字母均小写

3.方法声明

方法声明语法:
在这里插入图片描述
例子:
在这里插入图片描述

同时方法声明的语法说明如下:
1.方法类型表示: 要么是+要么是-。+就代表是类方法,在调用方法是必须用类来调用。-就代表是实例方法,调用时要用实例来调用,实例可以理解成用类来创建的对象。
2.方法返回值: 与C语言类似
3.方法签名关键字: 由方法名,形参标签,冒号组成

以我们的例子来说明:pname是我们的方法名,n是我们的传入的形参,NSString是我们的数据类型
注意 pname 与 pname: 是两种不同的方法
前者不带冒号说明是一个不带形参的方法
后者这是一个带有形参的方法

4.实现部分

在这里插入图片描述
1.实现部分的类名必须与接口部分的类名相同以确保是同一个类的接口部分以及实现部分
2.类实现部分必须为类声明部分的每个方法提供方法定义,同时在实现部分定义的方法,只能在类实现部分进行使用
在这里插入图片描述

二、对象的产生与使用

定义类之后我们就可以使用类了,我们要分以下三步来使用类

  1. 定义变量
    类名*变量名;(一定要记得有星号)
  2. 创建对象
    [[类名 alloc] init] (记得alloc关键字是写在里面的)
  3. 调用类方法
    蔡徐坤 打:篮球
    这很符合我们的自然语言的习惯,需要主谓宾。
    1. 蔡徐坤作为主语,也就是方法调用者,他既可以是类,也可以是实例。
    2. 打作为我们的的谓语,实际上就是方法
    3. 篮球作为宾语,实际上就是我们调用方法时需要传入的参数

tip:
在这里插入图片描述
4. 接口部分代码
在这里插入图片描述
5. 实现部分代码
在这里插入图片描述
6.对象的产生与使用
在这里插入图片描述

编译结果:
在这里插入图片描述

三、对象与指针

我们在前面的.m代码中有这样一行代码FKperson* person = [[FKperson alloc] init];
这行代码创建了一个EKPerson实例,也被称为EKPerson对象。然后我们将这个对象赋给变量person。

这行代码实际上产生了两个东西:一个是变量,一个是实例。
在这里插入图片描述

可以看出不同内存块分别存储了不同的成员变量。 当我们把对象赋给EKPerson*
变量时,实际上让这个变量指向了对象在内存中的首地址。因为EKPerson* 变量实际上是一个指针类型的变量。
形象的说,可以认为这个变量指向实际的对象

在这里插入图片描述

四、self关键字

1.避免重复创建

OC中提供了一个self关键字,self关键字总是指向调用该方法的对象
self关键字的最大作用是让类中的一个方法访问该类的另一个方法或成员变量,从此我们可以理解我们的self关键字一般是在实现部分使用
在讲解self前我们先来看一个问题
在这里插入图片描述

因为我们的方法必须由对象或者类调用,因此我们在run方法中调用jump方法时,正常的思路应该是先创建一个对象,然后通过这个对象来调用其他方法。但实际上我们并不用那么麻烦。
因为我们在run方法中调用jump方法是一定需要一个对象的,但是我们不一定需要在主函数之外重新创建一个对象。
我们在调用run方法时,主函数一定会提供一个FKPerson对象。这样我们就可以直接调用使用这个已经存在的对象。 因此我们用self关键词来让run方法中获得调用该方法的对象,也就是主函数中创建的对象。

self总是代表当前类的对象,当出现在方法体时,它所代表的对象是不确定的,但是类型是确定的,它所代表的对象只能是当前类的实例。当我们在主函数中调用方法时,他的对象就确定了。谁调用这个方法,self就代表谁。
但是self却不能出现在类方法中,因为self所代表的是一个实例,而不是一个

接口部分:
在这里插入图片描述

实现部分:
在这里插入图片描述
主函数:
在这里插入图片描述
编译结果:
在这里插入图片描述
我们采用以上的方式定义类更符合逻辑。如果我们在run方法中又创建了一个新对象调用jump方法,就意味着我们执行run方法需要依赖另一个对象的jump方法,但实际上我们一个人就可以完成jump与run两个方法。
而用self则代表着当我们调用run方法时,需要依赖自己的jump方法,这更符合逻辑,也更符合self的含义–自己。

2.处理重名

当局部变量与成员变量重名时,局部变量会隐藏成员变量
在这里解释一下:局部变量就是我们传入的参数,也就是形参。成员变量就是我们在类接口部分定义的描述类的对象的状态数据
因为我们一般会让其重名,这边略看即可。
在这里插入图片描述

3.作为返回值

接口部分
在这里插入图片描述
在这里我们需要着重注意接口部分,因为我们的成员变量一般是不允许访问的,但是我们用了public关键字,就将public关键字下面的全部成员变量暴露给了程序,因此我们可以使用成员变量。

实现部分
在这里插入图片描述

main函数
在这里插入图片描述
编译结果
在这里插入图片描述

五、id类型

OC提供了一个id类型,这个id类型可以代表所有对象的类型。
也就是说所有实例都可以赋给id类型的变量
当通过id类型的变量来调用方法时,OC将会执行动态绑定。
在这里我们需要注意:OC将会在运行时判断该对象所属的类以及确定需要动态调用的方法,而非在编译时确定要调用的方法。

实现以及编译结果
在这里插入图片描述
重点:在运行时检测到该变量所需要指向对象的类型为FKPerson

六、方法详解

方法是类或对象行为特征的抽象。从功能上看,方法完全类似于函数。但是方法不能独立存在。要么属于类,要么属于对象

1.方法的所属性

在这里插入图片描述

2.形参个数可变的方法

在定义方法是在最后一个形参名后面增加逗号与三点(, …),则表明该形参可以接受多个参数值
在这里插入图片描述
我们声明了一个NSString*的形参,除了name参数之外,表示还可接受个数可变的NSString参数
为了获取个数可变的参数,我们还需要用如下关键字。

  1. va_list:这是一个数据类型,用于定义指向可变参数列表中的指针变量。
  2. va_start: 这是一个函数,该函数指定开始处理可变形参的列表并让指针变量指向可变形参列表中的第一个参数
  3. va_end:结束处理可变形参,释放指针变量。
  4. va_arg:返回获取指针当前指向的参数的值,并将指针移动到下一个参数。
    上面这些方法为test方法提供了实现。

实现部分代码

在这里插入图片描述

主函数及编译结果
在这里插入图片描述

在这里插入图片描述

七、成员变量

OC中根据定义变量的位置不同,将变量分为三大类:成员变量局部变量全局变量

全局变量:在前面,还没看,由函数演化而来。
局部变量:方法中定义的变量。
因为这两个在前面都有讲解但是笔者还没看,所以在这里着重讲解成员变量

1.成员变量及其运行机制

成员变量:在类接口部分或类实现部分定义的变量。
OC的成员变量都是实例变量,并不支持真正的类变量
实例变量从实例被创建开始存在,直至系统完全销毁这个实例。实例变量可理解为实例成员变量,它作为实例的一个成员,与实例共存亡
在这里插入图片描述
例:
在这里插入图片描述
与c语言不同,成员变量无需显式初始化,只要为一个类定义了实例变量,系统会为实例变量执行默认初始化。基本类型的实例变量默认初始化为0,指针类型的成员变量默认初始化为nil.

2.多个实例中的内存示意图

在这里插入图片描述

在这里插入图片描述

3.模拟类变量

通过内部全局变量来模拟类变量
static修饰局部变量表示将该局部变量存储到静态存储区,修饰全局变量用于限制全局变量只能在当前源文件访问

为了模拟类变量,我们在实现部分定义一个static修饰的全局变量,并提供一个类方法来暴露全局变量。
我们在接口部分声明两个类方法分别用于修改与获取类变量

实现部分:
在这里插入图片描述

主函数与编译结果:
在这里插入图片描述

在这里插入图片描述

4.单例(Singleton)模式

如果一个类始终只能创建一个实例,则这个类被称为单例子类
单例类可通过static全局变量来实现,程序考虑定义一个static全局变量,该变量用于保存已创建的Singleton对象
每次程序获取该实例时,程序会先判断static全局变量是否为nil,如果为nil则初始化一个实例并赋值给全局变量

实现部分:
在这里插入图片描述
我们通过使用instance方法来获取Singleton实例时,程序最多只会产生一个Singleton实例,我们在main函数中测试这个类时,将可以看到我们产生的singleton对象实际上是同一个对象

八、总结

下面我们来温习一下学过的知识

  1. 首先我们的OC的面向对象语言有两个很重要的概念类与对象。类是对一批对象的抽象
  2. 我们定义类分为接口部分与实现部分
    接口部分:我们需要定义成员变量与方法。方法由分为+,-两种类型。其中+标识的方法只能通过类来调用,也叫做类方法。-标识的方法只能通过实例(对象)来调用,也叫做实例方法。下面来举几个例子: - (void) setName:(NSString*) name;setName是方法名,(NSString*)是传入参数的数据类型, n就是传入的形参。
    实现部分:实现部分就是实现我们在接口部分定义的方法,同时我们一般需要保证我们的局部变量与成员变量不会重名,否则将会引入self关键字,那么就会变得很麻烦。
  3. 我们实现类的定义后接下来我们就要使用类了
    使用类又分为定义变量,创建对象,调用类方法。
    其实定义变量与创建对象可以连在一起使用: 类名* 变量名 = [[类名 alloc] init];
    这里我们又要知道该如何调用类方法,这一般遵循我们自然语言的主谓宾的原则,格式为[方法调用者 方法:传入的参数]。
    在这里我们的方法调用者可以是类也可以是实例创建的变量。
  4. 然后我们又学习了对象与指针的用法,这其实和c语言中的链表十分类似,我们EKperson* p = [[EKperson alloc] init];这行代码创建了两个东西,一个是实例,一个是变量,我们将我们创造的实例的地址赋给变量,p实际上是一个指针变量,指向了实际的对象
  5. 同时我们了解了self关键字,self关键字的作用就是当我们在实现部分内部调用方法时,一般来说我们的方法调用者是实例或者类,所以一般思路是先创建一个指针变量指向实例或者类,然后再去调用方法,self就帮我们解决了这个问题,因为我们在主函数中调用方法就已经产生了一个实例,当我们在一开始在方法中使用self时,他的对象是不确定的,但是他的类型是确定的,当主函数中创建实例调用这个方法时,他的对象就确定了,谁调用这个方法,self就代表谁。 其实这样更符合我们的逻辑,我们调用方法时应该是通过自己本身调用,而不是通过在创建另外一个对象来调用
  6. id类型是在我们的程序运行过程中会自动确定我们创建的指针变量的所属类型,也就是说所有实例都可以赋给id类型的变量.。这是一个动态的过程
  7. 方法的所属性与函数类似,但是方法只能在类体中创建,同时不能独立存在,必须依靠对象调用。
    当我们想使形参数量可变时,会在形参的后面加上“,…”.
  8. 成员变量类似于我们c语言中定义的结构体,但是我们在定义成员变量时无需初始化,基本数据类型自动为0,指针变量类型自动为nil。
  9. OC中并没有为类定义变量,所以我们需要模拟类变量,步骤简单来说就是在实现部分定义一个static全局变量,然后通过类方法来获取这个全局变量,这样做的目的是让主函数能够获得在实现部分定义的变量
  10. 单例模式是指一个类只能创建一个实例,这个类叫做单例子类。单例类需要通过定义一个static全局变量来实现。我们首先将它定义为nil,然后给出我们的代码:
static id instance = nil;//注意这里创建的实例,而不是指针变量
+ (id)instance {
    if(!instance)
        instance = [[FKperson alloc] init];
    return instance;
}

id的类型会在运行时创建对象的类确定。注意是所有实例都可以赋给id类型的变量,但必须在创建实例时告诉id他所属的类型。

  1. 最后还有需要着重注意的一点就是==我们定义的- (void)setName:(NSString*)name Age:(int)age;与- (void)setName:(NSString*)name;==并不是同一种方法,它们各自都是一种方法。这点需要着重理解一下。
  2. 同时我们又以上面为例,我们源码中对方法进行定义与实现的标准格式在方法标识符与前一个形参标签结束后会添加空格,其余皆不添加空格。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值