类的定义
首先在学习类之前,我们必须知道什么是类。所谓类就是一种自定义数据类型,可以使用类来定义变量,这种类型的变量相当于指针类型的变量。也就是说,所有类都是指针类型的变量。我们也可以将类理解为具有统一特征的一群对象。
对象的定义
所谓对象,简单的理解,其实就是具体的实体。这就和类联系了起来,因为类是一种抽象的存在,所以对象就是由抽象引发出来的实体,举个例子,我们经常说人类,人类就是一种类,而我们如果说人,就是由人类引出来的实体。
定义类
组成
要想定义一个类,我们需要有两个步骤。分别是:接口部分,实现部分。
接口部分:定义该类包含的成员变量和方法。
实现部分:为该类的方法提供实现。
定义接口部分的语法,代码如下:
@interface MyClass : NSObject //其中MyClass是类名,NSObject是父类
{
int _count;
id _data;
NSString* _name;
}//成员变量声明
- (id)initWithString:(NSString*)aName;
+ (MyClass*)createMyClassWithString:(NSString*)aName;//方法声明
@end
其中@interface是用于声明定义类的接口部分,@end表明定义结束。
成员变量:用于描述该类的对象的状态数据。
方法:用于描述该类的行为。比如说我们定义类一个人,程序需要关心人具有工作行为,那么程序就应该为人这个类声明工作方法。
一般来说,我们会将定义类的接口声明部分放在头文件中。也就是说,定义类接口部分的源代码应该命名为*.h 文件。
方法声明的语法
方法类型标识:该标识符要么是+,要么是-,其中,+代表该方法是类方法,直接用类名即可调用。-代表该方法是实例方法,必须用对象才能调用。
方法返回值类型:返回值类型可以是OC允许的任何数据类型,包括基本类型、构造类型和各种指针类型。如果声明了方法返回值类型,则方法体内必需有一个有效的return语句,该语句返回一个变量或一个表达式,这个变量或者表达式必须与此处声明的类型匹配。除此之外,如果一个方法没有返回值,则必须使用void来声明。
方法签名关键字:OC的方法签名关键字由方法名、形象标签和冒号组成。方法名命名规则与成员变量命名规则相同,但不需要以下划线开头,通常建议方法名以英语动词开头。除第一个形参外,OC建议为后面的每个形参都指定一个“形参标签”,该形参标签可以很好的说明该形参的作用。另外一旦在定义方法时指定了形参列表,则调用该方法时必须传入对应的参数值——谁调用方法,谁负责为形参赋值。
方法
-(void)eat; //无参数无返回值
-(double)square:(double)number;//一个参数
-(void)setName:(NSString *)name andAge:(int) age;//多个参数
方法实现
-(void)eat{
NSlog(@"very good");
}
-(double)square:(double)number{
return number * number;
}
-(void)setName:(NSString *)name andAge:(int) age
{
_name = name;
_age = age;
}
对象
定义类之后,接下来就可以使用该类了,可从如下3方面来使用类。
1.定义变量。
2.创建对象。
3.调用类方法。
定义变量的语法为:
类名 * 变量名;
创建对象的语法为:
[[类名 alloc] 初始化方法];
在上面的语法格式中,alloc是OC的关键字,该关键字负责为该类分配内存空间,创建对象。除此之外,还需要调用初始化方法对该实例执行初始化。由于所有的对象都继承了NSObject类,因此所有的类都有一个默认的初始化方法:init。
比如我们定义一个FKPerson类的用法:
FKPerson * person;
person = [[FKPerson alloc] init];
OC调用方法的语法格式为:
[调用者 方法名 :参数 形象标签 :参数值 …];
如果方法声明中声明了多个形参,那么调用方法时需要为每个形参传入相应的参数值。
另外如果访问权限允许,OC允许直接通过对象来访问成员变量。格式如下:
对象->成员变量名
让我们来定义一个FKPerson类。
//先定义一个FKPerson *类型的变量
FKPerson* person;
//创建FKPerson对象,赋给person变量
person = [[FKPerson alloc] init];
上面的代码也可以简写为:
FKPerson* person = [[FKPerson alloc] init];
在OC程序中,一般只导入类的接口部分,也就是只会使用#import"FKPerson.h",不会导入类的实现部分,因此,此时需要使用如下命令来编译该程序:
clang - fobjc - arc - framework Foundation FKPerson.m FKPersonTest.m
通过类来调用类
//调用有参数的方法, 必须传入参数
[person say:@"Hello, I love iOS"];
[person setName: @"孙悟空" andAge: 500];
//调用无参数的方法,不需要传入参数
//方法有返回值,可以定义一个类型匹配的变量,来接收返回值
NSString* info = [person info];
NSLog(@"person的info信息为: %@", info);
//下面调用test的方法会引起错误,因为test方法是在实现部分定义的,应该被隐藏
//[person test];
//通过类名来调用类方法
[FKPerson foo];
对象和指针
首先我们先看一行代码:
FKPerson * person = [[FKPerson alloc] init];
这行代码创建了一个FKPerson实例,也被称为FKPerson对象,这个FKPerson对象被赋给person变量。
这行代码中实际产生了两个东西: 一个是person变量,一个是FKPerson对象。
从FKPerson类定义来看,FKPerson对象应该包含了3个成员的变量,两个可以暴露的成员变量:person setName: @“孙悟空” andAge: 500,和一个被隐藏的成员变量。而成员变量是需要内存来储存的。因此,在创建FKPerson对象时,必须头对应的内存来储存FKPerson对象的成员变量。
如图所示:
由此可见,其实FKPerson对象是由许都块内存组成,不同的内存块存储了不同的成员变量。当把这个FKPerson对象赋值给一个FKPerson变量时,因为FKPerson类型的变量本质就是一个指针变量,所以person变量仅仅只是存储了FKPerson对象在内存中的首地址。形象的说,可以认为FKPerson*类型的变量指向实际的对象。
从本质来说,类也是一种指针变量,因此,程序中定义的FKPerson*类型只是存放一个地址值,它该保存在该main()函数的动态存储区,它指向实际的FKPerson对象,而真正的FKPerson对象则放在堆(heap)内存中。
也就是说,在main()方法的动态存储区保存的指针变量只想保存了该对象的地址,并非该对象的成员变量数据。当我们调用person指针变量的成员变量和方法时,实际上是访问person所指向对象的成员变量和方法。简单的来说,我们调用person指针变量,先获得的是这个指针变量保存的对象的地址,通过地址我们才找到了真正的对象,然后访问对象的成员变量和方法。
FKPerson *p2 = person;
上面的代码,我们又定义了一个新的FKPerson*变量,同时将原本person中存储的地址值又赋给了p2。此时,我们无论是访问person的成员变量和方法,还是访问p2的成员变量和方法,都访问的是FKPerson对象的成员变量和方法,将会返回相同的访问结果。
总结
其实定义一个类就是为了重复的去创建该类的实例,也就是对象,因为一个类就是多个具有相同特征的实例,当我们创建了一个类,我们就可以去定义对象,比如我们定义了水果类,而苹果,西瓜,芒果都是这个类的对象,都是从抽象的概念引出的实例。