习惯了java中的类只有一个文件,刚开始用OC不太习惯,用一段时间就好了。
一 简单语法
1 类
java中我们使用一个.java文件来描述一个类,但在OC中使用两个文件。
- .h文件:类的声明文件,用于声明变量、方法、属性。使用@interface和@end来声明。它只是一个声明文件,并不能写功能。
- .m文件:类的实现文件,用于实现.h文件中声明的方法,也可以实现其他方法。使用@implementation和@end声明。
2 方法
java中有动态方法(无static)和静态方法(staic修饰的)。OC中也有,它的区分比较个性。
+ 表示类方法,即静态方法,可以直接使用类名调用。
- 表示对象方法,即动态方法,只能使用对象调用。
声明方法和实现方法,都使用+或-开头。
.h文件中的方法权限是public的。
3 成员变量
成员变量的作用域有三种:
@public 全局都可以访问
@protected 只能在类内部和子类中访问
@private 只能在类内部访问
二 Xcode创建一个类
1 右击项目文件夹,选择“New File”。
2 选择OS X—— Source—— Cocoa Class。
3 输入类名,父类和使用的语言。
这里类名是Phone,父类是NSObject,Language有俩种:Objective-C和swift语言,这里我们使用Objective-C。
4 创建完毕后,项目中多了两个文件。
这两个文件名 和类名一致。
Phone.h文件是声明文件,Phone.m是实现文件。
三 第一个类代码解析
1. Phone.h——类的声明文件
#import <Foundation/Foundation.h>
@interface Phone : NSObject
@end
第一行导入了Foundation框架中的Foundation.h文件,NSObject被声明在Foundation.h中。
2. Phone.m——类的实现文件
#import "Phone.h"
@implementation Phone
@end
第一行首先导入它的声明文件Phone.h,如果不导入这个肯定会有错的。。。。
四 设置成员变量
1 添加一个成员变量
#import <Foundation/Foundation.h>
@interface Phone : NSObject{
int price;
}
@end
添加成员变量后我们发现多了一个{ }大括号。这是应该的,如果没有声明成员变量可以省略括号,如果有成员变量则需要写大括号,并且把成员变量写在括号中。声明的变量price默认访问权限是protected,也就是可以在Phone类内部和子类中使用。
2 再添加几个不同作用域的变量
#import <Foundation/Foundation.h>
@interface Phone : NSObject{
int price;
@public
float length;
float weight;
@protected
int time;
float version;
@private
float size;
}
@end
@protected作用域的有:price time version
@private作用域的有:size
五 添加方法
上面的的成员变量中有protected权限的,外界不能直接访问它。为了保证面向对象数据的封装性,我们可以提供price的get方法和set方法,让外界间接访问price。现在我们添加price的get和set方法。
#import <Foundation/Foundation.h>
@interface Phone : NSObject{
int price; //价格
@public
float length; //长度
float weitht; //宽度
@protected
int time; //上市时间
float version; //版本号
@private
float size; //屏幕尺寸
}
//price的set方法
- (void)setPrice:(int)newPrice;
//price的get方法
- (int)price;
@end
第17行声明里price的get方法,方法名为price,OC建议get方法的名字跟成员变量保持一致(如果是在Java中,就应该叫做getPrice)。第17行最面的 - 表示这是一个动态方法( + 则表示静态方法)。price前面的(int)表示方法的返回值为int类型,方法的返回值和参数类型都需要用小括号()包住。
第15行声明了price的set方法,前面的 - 表示动态方法,(void)表示方法没有返回值
在OC方法中,一个冒号:对应一个参数。由于第15行price的set方法接收一个int类型的参数,参数名为price,所以(int)newPrice前面有一个冒号:
一定要记住:一个冒号:对应一个参数,而且冒号:也是方法名的一部分。因此第15行set方法的方法名是setPrice:,而不是setPrice
再加大一下难度,假如增加一个方法可以同时设置price和time,那么就应该这样写:
1 - (void)setPrice:(int)price andTime:(int)newTime;
* 这个方法是动态方法、没有返回值,接收2个参数,所以有2个冒号:
* 这个方法的方法名是setPrice:andTime:
* 其实andSize是可以省略的,它只是为了让方法名念起来通顺一点,也让(int)newTime前面的冒号:不那么孤单
2 实现方法
#import "Phone.h"
@implementation Phone
//price的get方法
- (int)price{
return price;
}
//price的set方法
- (void)setPrice:(int)newPrice{
price = newPrice;
}
- (void)setPrice:(int)newPrice andTime:(int)newTime{
price = newPrice;
time = newTime;
}
@end
上面实现了刚才点三个方法。
六 创建对象
#import <Foundation/Foundation.h>
#import "Phone.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Phone *ph = [[Phone alloc] init];
[ph release];
}
return 0;
}
1 包含Phone.h
因为要用到Phone这个类,所以包含了它的头文件。#import "Phone.h"
2 创建对象
在Java中是使用关键字new来创建对象,比如new Phone(),其实这句代码做了2件事:
- 给对象分配存储空间
- 调用Phone的构造方法进行初始化
在OC中创建对象也需要按顺序做上面所述的2件事
1)调用Phone类的静态方法alloc分配存储空间
Phone *ph = [Phone alloc];
- OC是方法调用是用中括号[ ],方法调用者写在括号左侧,方法名写在括号右侧,中间留点空格。因此上面是调用了Phone类的静态方法alloc。
- 上面调用的alloc方法会返回分配好内存的Phone对象,在等号左边用了一个指向Phone类型的指针变量ph来接收这个对象,注意stu左边的*号。所有OC对象都是用指针变量来接收的,如果你不了解指针,你记住下面这点就行了:利用类名定义一个变量时,类名后面一定要带个*号。
- alloc方法是这样声明的:
+ (id)alloc;
可以看到,它的返回值类型是id,这个id代表任何指针类型,你可以暂时理解为:id可以代表任何OC对象,类似于NSObject *。
2)调用Phone对象的构造方法init进行初始化
前面调用alloc方法返回的Phone对象ph是不能正常使用的,因为仅仅是分配了内存,并没有进行初始化,接下来调用对象的init方法进行初始化
ph = [ph init];
看清楚了,由于init是动态方法,所以这里使用ph变量来调用,并不是使用类名来调用。init会返回已经初始化完毕的对象,再次赋值给了ph变量。这时候的Phone对象ph才能正常使用。
3)其实,我们最常见的做法是将alloc和init连起来使用:
Phone *ph = [[Phone alloc] init];
相信有面向对象开发经验的你一眼就能看懂了,在main.m完整代码的第7行。
3 销毁对象
由于OC不支持垃圾回收,因此当不再使用某个对象时,需要调用对象的release方法释放此对象。我们在第9行销毁了stu对象。
[ph release];
这个release方法在这里调用一次即可,不要觉得多调用多几次,对象就会释放地干净一点,这样做会很危险,容易造成野指针错误。
4 其他
1> 也可以调用静态方法new快速创建一个对象
1 Phone *ph = [Phone new]; 2 3 [ph release];
不过我们还是习惯使用alloc和init来创建对象
2> 前面我们调用了Phone的alloc、init、new方法,但是你会发现Phone.h中并没有声明这些方法,为什么能够调用呢?原因很简单,这些方法都是父类NSObject的,子类当然可以调用父类的方法。
七 访问公共成员和方法
#import <Foundation/Foundation.h>
#import "Phone.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Phone *ph = [[Phone alloc] init];
// 访问公共变量length
ph->length = 10.0f;
// 调用setPrice:方法设置变量Price的值
[ph setPrice:2700];
// 调用setPrice:andTime:方法同时设置变量age和height的值
[stu setPrice:1999 andTime:2014];
// 访问公共变量length
int length = ph->length
// 调用price方法获取变量price的值
int price = [ph price];
// 打印no和price的值
NSLog(@"length is %f and price is %i", length, price);
[ph release];
}
return 0;
}
1.第7行创建了Phone对象,第26行销毁了对象
2.第10行和第19行访问了Phone对象的公共成员变量length,如果不是公共变量,不能像这样直接访问。注意访问方式:对象->成员变量
3.第13行调用了Phone对象的setPrice:方法,传入参数27修改了成员变量price的值
4.第16行调用了Phone对象的setPrice:andTime:方法,同时修改了成员变量price和time的值
5.第21行调用了Phone对象的price方法获取成员变量price的值
6.第24行输出了price和length的值,输出结果:
2015-12-05 21:54:56.221 Hello Object-C[1276:303] length is 10.000000 and price is 1999