OC语法之-类和对象
类
- 完整的描述一个类:
- 完整地写一个函数:函数的声明和定义(实现)
- 完整地写一个类:类的声明和实现
- 类名的命名规范:有意义、驼峰标识、首字母大写
- 类名\属性的命名规则:标示符的规则
- 类的声明:
- 作用:声明对象的属性、行为
- 必须
:NSObject
- 作用:让Car类具备创建对象的能力
- 成员变量:
- @interface的大括号{}中声明的变量:wheels、speed
- @interface的大括号和函数的大括号是不一样的,他是用来声明对象属性(实例变量\成员变量,默认会初始化为0)
- @public可以让Car对象的wheels和speed属性被外界访问
- 行为:
- 也叫方法,方法名、参数、返回值(声明、实现)
- 只要是OC对象的方法,必须以减号 - 开头
- OC方法中任何数据类型都必须用小括号()扩住
- OC方法中的小括号():括住数据类型
- 注意跟C语言
- 类的实现:
- 作用:
- 用来实现@inteface中声明的行为(方法)
- 方法的实现(说清楚方法里面有什么代码)
// 因为使用了NSObject #import <Foundation/Foundation.h> // 1.类的声明 // 声明对象的属性、行为 // : NSObject 目的是:让Car这个类具备创建对象的能力 @interface Car : NSObject {// 用来声明对象属性(实例变量\成员变量,默认会初始化为0) // @public可以让外部的指针间接访问对象内部的成员变量 @public int wheels; // 轮胎个数 int speed; // 时速(xxkm/h) } // 方法(行为):方法名、参数、返回值(声明、实现) // 只要是OC对象的方法,必须以减号 - 开头 // OC方法中任何数据类型都必须用小括号()扩住 // OC方法中的小括号():括住数据类型 - (void)run; @end // 2.类的实现 // 用来实现@inteface中声明的方法 @implementation Car // 方法的实现(说清楚方法里面有什么代码) - (void)run { NSLog(@"车子跑起来了"); } @end
- 作用:
创建对象
- 用一个类来创建对象
- 在OC中,想执行一些行为,就写上一个中括号[行为执行者 行为名称]
[Car new];
执行了Car这个类的new行为来创建新对象- OC中只能用指针来操纵对象
- [Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址)
- 定义一个指针变量p,p将来指向的是Car类型的对象
- 设置一个对象的属性
- 跟用指向结构体的指针访问结构体属性一样,用
->
,代码如下:
int main() { // 在OC中,想执行一些行为,就写上一个中括号[行为执行者 行为名称] // 利用类来创建对象 // 执行了Car这个类的new行为来创建新对象 // 定义了一个指针变量p,p将来指向的是Car类型的对象 // [Car new]每次都会创建出一个新对象,并且会返回新对象本身(新对象的地址) Car *p = [Car new]; //外部的指针间接访问对象内部的成员变量,必须有@public修饰 // 给p所指向对象的wheels属性赋值 p->wheels = 4; p->speed = 250; // 给p所指向对象发送一条run消息 [p run]; Car *p2 = [Car new]; p2->wheels = 5; p2->speed = 300; [p2 run]; NSLog(@"车子有%d个轮子,时速位:%dkm/h", p->wheels, p2->speed); return 0; }
注意:以上代码都在一个.m文件中
- 跟用指向结构体的指针访问结构体属性一样,用
方法与成员变量
- 在对象方法中直接可以拿到当前类的成员变量,来使用
- 原理:当对象调用方法时,方法中使用到成员变量时,就会到方法调用者这个对象中找相应的成员变量
#import <Foundation/Foundation.h> @interface Person : NSObject { @public int age; double weight; } - (void)walk; @end // 2.类的实现 @implementation Person // 实现@interface中声明的方法,可以直接使用成员变量 - (void)walk { NSLog(@"%d岁、%f公斤的人走了一段路", age, weight); } @end int main() { Person *p2 = [Person new]; p2->age = 30; p2->weight = 60.0; [p2 walk]; return 0; }
对象的简单内存分析
- 类在内存中也会占据存储空间
- 当你第一次使用类的时候,系统就会给这个类分配内存,把这个类加载进内存
- 即你第一次使用类创建对象时,它是先把这个类加载进内存,然后创建对象,并且整个程序运行中,类只会加载一次
- 整个程序中类只占用一份存储空间,同一种类创建多个对象,就会分配多个对象的内存.
- 类里面只存放方法列表(类方法和对象方法)
- 每个对象内部都有一个isa指针,指向它所对应的类
- 因此,当你用一个类创建很多对象,并且用这些对象调用方法时,所有的对象都公用一个方法
- 成员变量是每个对象独有的,比如:对象p1的成员变量age是10,对象p2的成员变量age是20;
- 调用方法做了什么事情?
[p eat];
- 给指针表变量p(对象)发送一条eat消息
- 对象p会根据isa指针找到对应的类
- 在类中找到相应的方法调用对应的代码
- 代码中用到对象的age跟weight,就回访问当前对象的age跟weight
对象与函数参数
- 对象成员变量作为函数参数
- 指向对象的指针作为函数参数
- 修改指向指向对象的成员
- 修改指针的指向
#import <Foundation/Foundation.h> @interface Car : NSObject {// 成员变量 @public int wheels; int speed; } - (void)run; @end @implementation Car - (void)run { NSLog(@"%d个轮子,速度为%dkm/h的车子跑起来", wheels, speed); } @end void test(int w, int s) { w = 20; s = 200; } void test1(Car *newC) { newC->wheels = 5; } void test2(Car *newC) { //对象2 Car *c2 = [Car new]; c2->wheels = 5; c2->speed = 300; newC = c2; newC->wheels = 6; } int main() { //对象1 Car *c = [Car new]; c->wheels = 4; c->speed = 250; //值传递,不会改变 test(c->wheels, c->speed); //指针传递会改变 test1(c); //此时的c指针仍然指向对象1,只是newC这个指针指向了对象2 //结果仍不变 test2(c); [c run]; return 0; }
常见错误
- 只有类的声明,没有类的实现
- 漏了@end
- @interface和@implementation嵌套
- 两个类的声明嵌套
- 成员变量没有写在括号里面
- 方法的声明写在了大括号里面
- 成员变量不能在{}中进行初始化、不能被直接拿出去访问
- 方法不能当做函数一样调用
- 成员变量\方法不能用static等关键字修饰,别跟C语言混在一起
- 类的实现可以写在main函数的后面,主要在类的声明后面就行了
OC方法跟函数的区别
- OC对象方法方法:
- 对象方法都是以减号 - 开头
- OC方法只能声明在@interface和@end之间,只能实现在@implementation和@end之间。也就是说OC方法不能独立于类存在
- 对象方法只能由对象来调用
- 对象方法归类所有
- 低级错误:方法有声明,但是实现的时候写成了函数
- C函数
- 函数能写在文件中的任意位置(@interface和@end之间除外),函数归文件所有
- C函数不属于类,跟类没有联系,C函数只归定义函数的文件所有
- 函数调用不依赖于对象
- C函数不能访问OC对象的成员
- OC的方法注意
- 方法只有声明,没有实现(经典错误)
- 方法没有声明,只有实现(编译器警告,但是能调用,OC的弱语法)
- 编译的时候:访问没有的成员变量直接报错,访问没有的方法,只是警告
#import <Foundation/Foundation.h> @interface Car : NSObject {// 成员变量\实例变量 //int wheels = 4; 不允许在这里初始化 //static int wheels; 不能随便将成员变量当做C语言中的变量来使用 @public int wheels; } - (void)run; - (void)fly; @end int main() { // wheels = 10; Car *c = [Car new]; c->wheels = 4; //oc方法调用 //方法调用依赖于对象 //run();错误 [c run]; //函数调用 void test2(); //调用不依赖于对象 test2(); return 0; } @implementation Car - (void) fly { } /* //函数不可以访问成员变量 void test2() { NSLog(@"调用了test2函数-%d", wheels); }*/ //函数可以写在任何地方 void test() { NSLog(@"调用了test函数"); } //对象方法方法 - (void)run{ test(); NSLog(@"%d个轮子的车跑起来了", wheels); } @end
@implementation
- 没有@interface,只有@implementation,也是能成功定义一个类的(仅仅在编译时会有警告而已),就像C语言的函数,可以没有函数的声明,只要把函数定义写在调用者的前面就行了
- @implementation中不能重复声明和@interface一样的成员变量(也就是说,如果interface中声明了一个成员变量,那么@implementation中不能重复)
- 声明成员变量不仅可以在@interface中的{}中,也可以在@implementation中的{}中
代码举例:
#import <Foundation/Foundation.h>
//在implementation也可以声明成员变量
@implementation Car : NSObject
{
@public
int wheels; // 多少个轮子
int speed; // 时速
}
- (void) run {
NSLog(@"%i个轮子,%i时速的车子跑起来了", wheels, speed);
}
@end
int main(){
Car *p = [Car new];
p->wheels = 4;
p->speed = 100;
[p run];
return 0;
}
OC方法
//无返回值,无参数
-(void)run;
//无返回值有参数
-(void)run:(int)speed;
//有返回值无参数
-(int)run;
//有返回值有参数
-(int)run:(int)speed;
//有返回值多参
- (int)sumWithNum1:(int)num1 andNum2:(int)num2;`
- 方法声明中的小括号”()”仅仅是用来括住数据类型
- OC方法中,一个参数对应一个冒号
- 冒号也是方法名的一部分
- 每个参数之间一定要用空格隔开
- 一个类中不允许有同名的方法
方法的声明
- 方法的声明不一定必须要有,方法的实现一定要有.
- 如果一个方法,只在当前类中使用,而不再外部使用时,可以不用声明方法
- 如果一个方法会在外部(main函数或者其他类,非当前类)使用,一定要有方法的声明,否则:1.虽然不会报错,但会警告 2.写代码时xcode不会提示.
代码举例:
#import <Foundation/Foundation.h>
@interface Car : NSObject
//在外部会使用,要声明
- (void)run;
@end
@implementation Car
- (void)run {
[self test];
NSLog(@"车子跑起来了");
}
//不在外部使用,不用声明
-(void)test {
NSLog(@"我仅仅在当前类内部使用");
}
@end
int main() {
Car *p = [Car new];
[p run];
return 0;
}
匿名对象
- 所谓的匿名对象就是创建一个对象不用指针指着他了
- 属性访问
[Car new]->speed = 200;
- 方法调用
[[Car new] run];
- 注意: 这两个
[Car new]
可不是同一个对象