- 封装:目前我的理解是将类声明里的实例变量“包裹”好,一旦“包裹”好,由类生成的对象也就“包裹”的“严实”了,这样外部的东西就不会随意修改对象的重要数据了,之前我们要想对类的实例变量赋值,是通过指针访问的,并且要在实例变量前面加上关键字@public,其实这个@public关键字像是把类里面的实例变量“赤裸裸”的暴露在外面,很不安全。那么不通过这种途径要想访问对象的数据咋办呢?set方法(setter)和get方法(getter)很好的解决了这一问题。
set方法:
- 它是对象方法,肯定是用减号'-'开头,方法名必须以set开头,紧跟着是实例变量名,并且首字母要大写
- 没有返回值,并且有参数,参数的类型跟对应的实例变量类型一致
- 能过滤掉一些不合理的赋值
get方法:
- 也是对象方法,必须用减号'-'开头,方法名不能以get开头,方法名和实例变量名一致(为了区分到底是方法名还是实例变量,以后所有实例变量都是以下划线‘_’开头)
- 有返回值,返回值类型与实例变量的类型一致,没有参数
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 { 5 int _age; //年龄 6 double _weight; // 体重 7 } 8 9 //年龄的setter和getter声明 10 - (void)setAge:(int)age; 11 - (int)age; 12 13 @end 14 15 @implementation Person 16 17 //年龄的setter和getter实现 18 - (void)setAge:(int)age{ 19 if (age <= 0) { 20 _age = 1; 21 }else{ 22 _age = age; 23 } 24 } 25 26 - (int)age{ 27 return _age; 28 } 29 30 @end 31 32 //测试age的setter和getter 33 int main(){ 34 Person *p = [Person new]; 35 [p setAge:-3]; 36 NSLog(@"年龄为%d", [p age]); 37 return 0; 38 }
- 继承:继承是OC语法最重要的一个特性,他是类之间的一种关系,如果说B类继承了A类,那么A类的所有实例变量和方法B类都会拥有而不需要重复说明省去了不必要的重复代码,上面的B类叫父类,A类成为子类,它的写法就是:“: 类名”。OC中基本所有的类都是继承NSObject类的,称NSObject为root Class(根类)
1 #import <Foundation/Foundation.h> 2 /************Person类************/ 3 @interface Person : NSObject 4 { 5 int _age; //年龄 6 double _weight; // 体重 7 } 8 9 //年龄的setter和getter声明 10 - (void)setAge:(int)age; 11 - (int)age; 12 13 @end 14 15 @implementation Person 16 17 //年龄的setter和getter实现 18 - (void)setAge:(int)age{ 19 if (age <= 0) { 20 _age = 1; 21 }else{ 22 _age = age; 23 } 24 } 25 26 - (int)age{ 27 return _age; 28 } 29 30 @end 31 32 /**************Studengt类***************/ 33 @interface Studengt : Person 34 35 @end 36 37 @implementation Studengt 38 39 @end 40 41 //测试Student类 42 int main(){ 43 Studengt *stu = [Studengt new]; 44 [stu setAge:17]; 45 NSLog(@"学生的年龄是%d", [stu age]); 46 47 return 0; 48 }
因为Studengt类继承了Person类,所以虽然子类没有明确说明实例变量和方法,但是学生依然有年龄_age的实例变量以及_age的getter和setter
但是继承有很多需要注意的地方:
- 如果父类和子类在同一个文件中,父类的声明和实现必须要在子类的前面,
- 子类拥有的新实例变量必须保证不能和父类的实例变量相同否则会报错:duplicate member
-
子类可以拥有和父类一样的方法,并且这个一样的方法可以在子类中只写实现,不用再写声明,我们称这种现象叫重写 。当一个的对象调用方法时,它首先会在自己里面找有没有该方法,如果没有,那么就去父类中找,直到找到为止。那么诱人会问了,子类和父类在内中具体是怎么联系在一起的?和前面提到的isa指针一样,每一个子类里都会有一个superclass指针指向它的父类。
- 一个子类只能有一个父类,这就好比生活中你只能有一个爸爸一样,当然父类是可以拥有多个子类的
- 这种继承关系耦合性太强(耦合性可以理解成没有了父类,子类就会崩溃)
- 使用场合也要注意,要符合了一定的逻辑,不能乱继承。比如说Student类里面有各种学习成绩,我们可以把这种成绩抽出来建一个Score类,但是这种情况下,我们不能将Student类继承Score类,我们只能说Student类包含或者说组合了Score类,此时的做法是将Score这个类作为Student类的实例变量。一般情况下如果从逻辑上有“A类是B类”这层关系,那么就能将A类作为B类子类,否则就不行。
- 多态:正式由于继承这个特性才导致了多态性比如:
1 #import <Foundation/Foundation.h> 2 /************Person类************/ 3 @interface Person : NSObject 4 5 @end 6 7 @implementation Person 8 9 @end 10 11 /**************Studengt类***************/ 12 @interface Student : Person 13 14 @end 15 16 @implementation Student 17 18 @end 19 20 21 int main(){ 22 // 创建一个Student类让Student类型的指针指向该对象 23 Studengt *stu = [Studengt new]; 24 25 return 0; 26 }
代码的第23行可以根据多态性(因为Student是继承了Person类)改为
1 Person *p = [Studengt new];
由于Person类又是继承根类NSObject,第23行又可以改为
1 NSObject *n = [Studengt new];
但是反过来是不行的
1 //错误代码 2 Student *stu = [Person new]; // 不能用子类类型的指针指向父类的对象
虽然不能这么写但是编译器在编译的时候不会报错,只会发出警告,这是由于OC的弱语法的特性导致的
但是要注意的是多态也有一定的局限性:父类类型的指针变量不能调用子类的方法
1 // 假设Person类里没有study方法,但是子类Student里面有 2 // 错误代码示范 3 int main(){ 4 5 Person *p = [Student new]; 6 // 因为p是父类类型的指针,所以它会去父类里找run方法 7 [p study]; 8 9 return 0; 10 }
虽然编译器编译的时候不会报错只会付出警告,但是对于初学者很不利,上述的错误我们可以采取强制转换措施
1 int main(){ 2 3 Person *p = [Student new]; 4 5 //强制把Person类型的指针转化为和stu同类型的指针,这里p和stu同时指向Student类对象 6 Student *stu = (Person *)p; 7 8 [p study]; 9 10 return 0; 11 }
- 补充:关于self和super的使用,首先self和super都是一种指针,self是指向当前调用者(方法调用者),可以理解成谁调用了当前方法(包括对象方法和类方法)self就指向谁,或者说是self出现在哪个方法里,self就只指向它;而super跟self差不多,只是super指向的时当前类的父类。代码实现self和super:
1 #import <Foundation/Foundation.h> 2 /************Person类*************/ 3 @interface Person : NSObject 4 5 - (void)test; 6 + (void)test; 7 8 @end 9 10 @implementation Person 11 12 - (void)test{ 13 NSLog(@"调用了父类的对象方法test"); 14 } 15 16 + (void)test{ 17 NSLog(@"调用了父类的类方法test"); 18 } 19 20 @end 21 /************Student类************/ 22 @interface Student : Person 23 24 - (void)study; // 学习方法声明 25 - (void)sleep; // 睡觉方法声明 26 + (void)playFootball; // 踢足球方法声明 27 + (void)playBasketball; // 打篮球方法声明 28 29 @end 30 31 @implementation Student 32 33 - (void)study{ 34 [super test]; // 调用当前类的父类test对象方法 35 //[self playBasketball];错误写法,因为在该类中找不到playFootball对象方法 36 NSLog(@"学习时间到!!!"); 37 } 38 39 - (void)sleep{ 40 [self study]; // 调用当前类的study对象方法 41 NSLog(@"睡觉时间到!!!"); 42 } 43 44 + (void)playFootball{ 45 [self playBasketball]; // 调用当前类的playBasketball类方法 46 // [self study]; 错误写法,因为找不到该类的study类方法 47 NSLog(@"足球时间到!!!"); 48 } 49 50 + (void)playBasketball{ 51 [super test]; // 调用当前类的父类的test类方法 52 NSLog(@"篮球时间到!!!"); 53 } 54 55 @end 56 57 58 int main(){ 59 // 创建学生对象 60 Student *stu = [Student new]; 61 62 [Student playFootball]; 63 [Student playBasketball]; 64 65 [stu sleep]; 66 [stu study]; 67 68 69 return 0; 70 }