类与对象
- 定义类
- 接口部分
- 定义成员变量
- 方法说明
- 实现部分
- 对象的产生与使用
- 对象与指针
- self关键字
- 避免重复创建
- id类型
- 方法详解
- 方法的所属性
- 形参个数可变的方法
- 成员变量
- 成员变量及其运行机制
- 多个实例中内存示意图
- 模拟类变量
- 单例模式
类是面向对象的重要内容,我们可以把类当成一种自定义的数据类型,可以使用类来定义变量,这种类型的变量相当于指针类型的变量。也就是说,所有的类都是指针类型的变量
定义类
面向对象程序设计中有两个很重要的概念:类与对象(也叫做实例)。类是对一批对象的抽象。类实际上是某种概念,人才是实体。例如我们日常接触到的同学, 实际上都是实例,可以理解成人这个集合当中的某个元素。
同时OC定义类分为两部分
接口部分:定义类所包含的成员变量以及方法
实现部分:为该类的方法提供实现
接口部分
interface的意思是接口,@interface用于声明定义类的接口部分。@end表示结束。
后面的花括号用于声明成员变量。花括号后面的部分用于声明该类的方法
成员变量:用于描述该类对象的状态数据,例如一个人的身高体重
方法:用于描述该类的行为,
同时接口声明放在h文件中
定义成员变量
与C语言类似,但是要以下划线"_" 开头。
同时成员变量应该以一个或多个有意义的单词连缀,一般遵循第一个单词首字母小写,后面每个单词首字母大写,其余字母均小写。
方法说明
同时方法声明的语法说明如下:
1.方法类型表示: 要么是+要么是-。+就代表是类方法,在调用方法是必须用类来调用。-就代表是实例方法,调用时要用实例来调用,实例可以理解成用类来创建的对象。
2.方法返回值: 与C语言类似
3.方法签名关键字: 由方法名,形参标签,冒号组成
以我们的例子来说明:pname是我们的方法名,n是我们的传入的形参,NSString是我们的数据类型
注意 pname 与 pname: 是两种不同的方法
前者不带冒号说明是一个不带形参的方法
后者这是一个带有形参的方法
实现部分
1.实现部分的类名必须与接口部分的类名相同以确保是同一个类的接口部分以及实现部分
2.类实现部分必须为类声明部分的每个方法提供方法定义,同时在实现部分定义的方法,只能在类实现部分进行使用
对象的产生与使用
定义类之后我们就可以使用类了,我们要分以下三步来使用类
定义变量
类名*变量名;(一定要记得有星号)
创建对象
[[类名 alloc] init] (记得alloc关键字是写在里面的)
调用类方法
蔡徐坤 打:篮球
这很符合我们的自然语言的习惯,需要主谓宾。
蔡徐坤作为主语,也就是方法调用者,他既可以是类,也可以是实例。
打作为我们的的谓语,实际上就是方法。
篮球作为宾语,实际上就是我们调用方法时需要传入的参数。
对象的产生与使用
对象与指针
我们在前面的.m代码中有这样一行代码
FKperson* person = [[FKperson alloc] init];
这行代码创建了一个EKPerson实例,也被称为EKPerson对象。然后我们将这个对象赋给变量person。
可以看出不同内存块分别存储了不同的成员变量。 当我们把对象赋给EKPerson*
变量时,实际上让这个变量指向了对象在内存中的首地址。因为EKPerson* 变量实际上是一个指针类型的变量。
形象的说,可以认为这个变量指向实际的对象
self关键字
避免重复创建
OC中提供了一个self关键字,self关键字总是指向调用该方法的对象。
self关键字的最大作用是让类中的一个方法访问该类的另一个方法或成员变量,从此我们可以理解我们的self关键字一般是在实现部分使用。
在讲解self前我们先来看一个问题
–因为我们的方法必须由对象或者类调用,因此我们在run方法中调用jump方法时,正常的思路应该是先创建一个对象,然后通过这个对象来调用其他方法。但实际上我们并不用那么麻烦。
–因为我们在run方法中调用jump方法是一定需要一个对象的,但是我们不一定需要在主函数之外重新创建一个对象。
–我们在调用run方法时,主函数一定会提供一个FKPerson对象。这样我们就可以直接调用使用这个已经存在的对象。 因此我们用self关键词来让run方法中获得调用该方法的对象,也就是主函数中创建的对象。
self总是代表当前类的对象,当出现在方法体时,它所代表的对象是不确定的,但是类型是确定的,它所代表的对象只能是当前类的实例。当我们在主函数中调用方法时,他的对象就确定了。谁调用这个方法,self就代表谁。
但是self却不能出现在类方法中,因为self所代表的是一个实例,而不是一个类
id类型
OC提供了一个id类型,这个id类型可以代表所有对象的类型。
也就是说所有实例都可以赋给id类型的变量
当通过id类型的变量来调用方法时,OC将会执行动态绑定。
在这里我们需要注意:OC将会在运行时判断该对象所属的类以及确定需要动态调用的方法,而非在编译时确定要调用的方法。
重点:在运行时检测到该变量所需要指向对象的类型为FKPerson
方法详解
方法是类或对象行为特征的抽象。从功能上看,方法完全类似于函数。但是方法不能独立存在。要么属于类,要么属于对象。
方法的所属性
形参个数可变的方法
在定义方法是在最后一个形参名后面增加逗号与三点(, …),则表明该形参可以接受多个参数值
我们声明了一个NSString*的形参,除了name参数之外,表示还可接受个数可变的NSString参数。
为了获取个数可变的参数,我们还需要用如下关键字。
va_list:这是一个数据类型,用于定义指向可变参数列表中的指针变量。
va_start: 这是一个函数,该函数指定开始处理可变形参的列表,并让指针变量指向可变形参列表中的第一个参数。
va_end:结束处理可变形参,释放指针变量。
va_arg:返回获取指针当前指向的参数的值,并将指针移动到下一个参数。
上面这些方法为test方法提供了实现。
成员变量
OC中根据定义变量的位置不同,将变量分为三大类:成员变量,局部变量,全局变量。
全局变量:在前面,还没看,由函数演化而来。
局部变量:方法中定义的变量。
因为这两个在前面都有讲解但是笔者还没看,所以在这里着重讲解成员变量
成员变量及其运行机制
成员变量:在类接口部分或类实现部分定义的变量。
OC的成员变量都是实例变量,并不支持真正的类变量
实例变量从实例被创建开始存在,直至系统完全销毁这个实例。实例变量可理解为实例成员变量,它作为实例的一个成员,与实例共存亡。
与c语言不同,成员变量无需显式初始化,只要为一个类定义了实例变量,系统会为实例变量执行默认初始化。基本类型的实例变量默认初始化为0,指针类型的成员变量默认初始化为nil.
多个实例中内存示意图
模拟类变量
通过内部全局变量来模拟类变量
static修饰局部变量表示将该局部变量存储到静态存储区,修饰全局变量用于限制全局变量只能在当前源文件访问
为了模拟类变量,我们在实现部分定义一个static修饰的全局变量,并提供一个类方法来暴露全局变量。
我们在接口部分声明两个类方法分别用于修改与获取类变量
单例模式
如果一个类始终只能创建一个实例,则这个类被称为单例子类。
单例类可通过static全局变量来实现,程序考虑定义一个static全局变量,该变量用于保存已创建的Singleton对象
每次程序获取该实例时,程序会先判断static全局变量是否为nil,如果为nil则初始化一个实例并赋值给全局变量
我们通过使用instance方法来获取Singleton实例时,程序最多只会产生一个Singleton实例,我们在main函数中测试这个类时,将可以看到我们产生的singleton对象实际上是同一个对象