在Java中,咱们用1个.java文件就能够描述清楚一个类;在OC中,通常用2个文件来描述一个类:html
1> .h:类的声明文件,用于声明成员变量、方法。类的声明使用关键字@interface 和@end 。java
注意:.h中的方法只是作一个声明,并不对方法进行实现。也就是说,只是说明一下方法名、方法的返回值类型、方法接收的参数类型而已,并不会编写方法内部的代码。ios
2> .m:类的实现文件,用于实现.h中声明的方法。类的实现使用关键字@implementation和@end 。函数
1> 方法的声明和实现,都必须以 + 或者 - 开头spa
+ 表示类方法(静态方法).net
- 表示对象方法(动态方法)指针
2> 在.h中声明的全部方法做用域都是public类型,不能更改code
成员变量的经常使用做用域有3种:orm
1> @public 全局均可以访问
2> @protected 只能在类内部和子类中访问
3> @private 只能在类内部访问htm
比Java少了一种做用域:包权限做用域,缘由很明显:OC没有包名的概念。
1.右击项目文件夹或者文件,选择"New File"
2.选择Cocoa的"Objective-C class"
3.输入类名和选择父类
这里的类名为Student,父类是NSobject
4.建立完毕后,项目中多了两个文件
* Student.h是类的声明文件,Student.m是类的实现文件
* 默认状况下,这2个文件的文件名跟类名一致
* 编译器只会编译.m文件,并不会编译.h文件
1 #import <Foundation/Foundation.h> 2 3 @interface Student : NSObject 4 5 @end
1> 看第3行,OC中使用关键字@interface来声明一个类,@interface后面紧跟着类名Student。
2> 类名Student后面的冒号":"表示继承,即第3行代码的意思是Student继承自NSObject。
3> 由于NSObject被声明在Foundation.h中,因此在第1行用#import包含了Foundation.h文件。
4> 第5行的@end表示类的声明结束了。@interface和@end是配套使用的。
1 #import "Student.h" 2 3 @implementation Student 4 5 @end
1> 看第3行,OC中使用关键字@implementation来实现一个类。@implementation后面紧跟的类名,表示究竟要实现哪个类。
2> 由于Student这个类是声明在Student.h中的,因此在第1行用#import包含了Student.h文件。若是你不包含Student.h,第3行代码确定报错,由于它根本不知道Student是个什么鬼东西。
3> 第5行的@end表示类的实现结束了。@implementation和@end是配套使用的。
正常状况下,咱们都是把成员变量定义在头文件中,也就是类的声明文件(.h)中
#import <Foundation/Foundation.h> @interface Student : NSObject{ int age; // 年龄 } @end
1> 第4行定义了一个int类型的成员变量age,age的默认做用域是@protected,便可以在Student类内部和子类中访问
2> 成员变量必须写在大括号{ }里面
接下来给Student增长几个不一样做用域的成员变量
#import <Foundation/Foundation.h> @interface Student : NSObject { int age; // 年龄 @public int no; // 学号 int score; // 成绩 @protected float height; // 身高 @private float weight; // 体重 } @end
一共有5个成员变量,其中
@public做用域的有:no、score
@protected做用域的有:age、height
@private做用域的有:weight
前面咱们定义了一个成员变量age,它的做用域是@protected,外界不能直接访问它。为了保证面向对象数据的封装性,咱们能够提供age的get方法和set方法,让外界间接访问age。接下来在Student中添加age的get方法和set方法。
#import <Foundation/Foundation.h> @interface Student : NSObject { int age; // 年龄 @public int no; // 学号 int score; // 成绩 @protected float height; // 身高 @private float weight; // 体重 } // age的get方法 - (int)age; // age的set方法 - (void)setAge:(int)newAge; @end
1> 第18行声明了age的get方法,方法名就叫作age,OC建议get方法的名字跟成员变量保持一致(若是是在Java中,就应该叫作getAge)
2> 第18行最面的 - 表示这是一个动态方法( + 则表示静态方法)。age前面的(int)表示方法的返回值为int类型,方法的返回值和参数类型都须要用小括号()包住
3> 第21行声明了age的set方法,前面的 - 表示动态方法,(void)表示方法没有返回值
4> 在OC方法中,一个冒号:对应一个参数。因为第21行age的set方法接收一个int类型的参数,参数名为newAge,因此(int)newAge前面有一个冒号:
5> 必定要记住:一个冒号:对应一个参数,并且冒号:也是方法名的一部分。所以第21行set方法的方法名是setAge:,而不是setAge
再加大一下难度,假如增长一个方法能够同时设置age和height,那么就应该这样写:
-(void)setAge:(int)newAge andHeight:(float)newHeight;
* 这个方法是动态方法、没有返回值,接收2个参数,因此有2个冒号:
* 这个方法的方法名是setAge:andHeight:
* 其实andHeight是能够省略的,它只是为了让方法名念起来通顺一点,也让(float)newHeight前面的冒号:不那么孤单
前面已经在Student.h中声明了3个方法,接下来一一实现它们
#import "Student.h" @implementation Student // age的get方法 -(int)age{ // 直接返回成员变量age return age; } // age的set方法 -(void)setAge:(int)newAge{ // 将参数赋值给成员变量age age = newAge; } // 同时设置age和Height的值 -(void)setAge:(int)newAge andHeight:(float)newHeight{ age = newAge;\ height = newHeight; } @end
第6行对age方法进行了实现,第12行对setAge:方法进行了实现,第18行对setAge:andHeight:方法进行了实现
若是是在Java中,一个Student.java文件就能够搞定成员变量和方法
public class Student { protected int age; protected float height; public int no; public int score; private float weight; /** * age的get方法 */ public int getAge() { return age; } /** * age的set方法 */ public void setAge(int newAge) { age = newAge; } /** * 同时设置age和height */ public void setAgeAndHeight(int newAge, float newHeight) { age = newAge; height = newHeight; } }
前面已经定义了一个Student类,成员变量和方法都有了,接下来看一下怎么使用这个类建立对象。
因为OC程序的入口点是main函数,因此在main.m文件中演示Student类的使用。
先上完整代码
#import <Foundation/Foundation.h> #import "Student.h" int main(int argc, const char * argv[]){ @autoreleasepool{ Student *stu = [[Student alloc] init]; [stu release]; } return 0; }
由于要用到Student这个类,因此在第2行包含了它的头文件
#import "Student.h"
给对象分配存储空间
调用Student的构造方法进行初始化
1)调用Student类的静态方法alloc分配存储空间
Student *stu = [Student alloc];
OC是方法调用是用中括号[ ],方法调用者写在括号左侧,方法名写在括号右侧,中间留点空格。所以上面是调用了Student类的静态方法alloc。
上面调用的alloc方法会返回分配好内存的Student对象,在等号左边用了一个指向Student类型的指针变量stu来接收这个对象,注意stu左边的*号。全部OC对象都是用指针变量来接收的,若是你不了解指针,你记住下面这点就好了:利用类名定义一个变量时,类名后面必定要带个*号。
alloc方法是这样声明的:
+ (id)alloc;
能够看到,它的返回值类型是id,这个id表明任何指针类型,你能够暂时理解为:id能够表明任何OC对象,相似于NSObject *。
2)调用Student对象的构造方法init进行初始化
前面调用alloc方法返回的Student对象stu是不能正常使用的,由于仅仅是分配了内存,并无进行初始化,接下来调用对象的init方法进行初始化
stu = [stu init];
看清楚了,因为init是动态方法,因此这里使用stu变量来调用,并非使用类名来调用。init会返回已经初始化完毕的对象,再次赋值给了stu变量。这时候的Student对象stu才能正常使用。
注:动态方法可使用对象来调用,而静态方法只能用类名来调用 ,[Student alloc] 如此方法。
3)其实,咱们最多见的作法是将alloc和init连起来使用:
Student *stu = [[Student alloc] init];
相信有面向对象开发经验的你一眼就能看懂了,在main.m完整代码的第7行。
注意:Student *stu 中的Student为指针变量类型,如 C语言中前边的指针变量的类型Int同样:
int b = 10;
int *p = &b;
因为OC不支持垃圾回收,所以当再也不使用某个对象时,须要调用对象的release方法释放此对象。咱们在第9行销毁了stu对象。
[stu release];
这个release方法在这里调用一次便可,不要以为多调用多几回,对象就会释放地干净一点,这样作会很危险,容易形成野指针错误。
1> 也能够调用静态方法new快速建立一个对象
1 Student *stu = [Student new]; 2 3 [stu release];
不过咱们仍是习惯使用alloc和init来建立对象
2> 前面我们调用了Student的alloc、init、new方法,可是你会发现Student.h中并无声明这些方法,为何可以调用呢?缘由很简单,这些方法都是父类NSObject的,子类固然能够调用父类的方法。
前面已经成功建立了一个Student对象,接下来访问一下它的公共变量和方法。
#import <Foundation/Foundation.h> #import "Student.h" int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] init]; // 访问公共变量no stu->no = 10; // 调用setAge方法设置变量age的值 [stu setAge:27]; // 调用setAge:andHeight:方法同时设置变量age和height的值 [stu setAge:20 andHeight:14.2f]; // 访问公共变量no int no = stu->no; // 调用age方法获取变量age的值 int age = [stu age]; // 打印no和age的值 NSLog(@"no is %i and age is %i", no, age); [stu release]; } return 0; }
1.第7行建立了Student对象,第26行销毁了对象
2.第10行和第19行访问了Student对象的公共成员变量no,若是不是公共变量,不能像这样直接访问。注意访问方式:对象->成员变量
3.第13行调用了Student对象的setAge:方法,传入参数27修改了成员变量age的值
4.第16行调用了Student对象的setAge:andHeight:方法,同时修改了成员变量age和height的值
5.第21行调用了Student对象的age方法获取成员变量age的值
6.第24行输出了age和no的值,输出结果:
2013-04-06 21:54:56.221 第一个OC程序[1276:303] no is 10 and age is 28
注:本文转自M了个J博客。