oc语言学习之基础知识点介绍(四):方法的重写、多态以及self、super的介绍
一、方法重写
/* 重写:当子类继承了父类的方法时,如果觉得父类的方法不适合,那么可以对这个方法进行重新实现,那么这个就重写。 注意:也就是说,一定只能发生在父类和子类关系中。 然后是子类重新实现父类的方法,绝对不是再写一个自己类的方法。 代码中原话叫:子类重写父类方法。 因为父类定义的方法不一定适用于子类。 注意:如果有重写,那么调用的是自己重写后的方法,如果没有重写,那么就调用的是父类的方法。 所以我们方法有一个执行的过程: 1.先去自己类里面找这个方法,如果找到就执行。 2.如果没找到,就去父类中找,如果找到就执行。 3.如果还是没找到,就去父类的父类中找,如果找到就执行。 4.如果没找到继续往上找,直到直到根类(NSObject)还没找到的话,就报错! 注意:子类可以重写父类的方法,但是,不能定义跟父类同名的成员变量 */ #import <Foundation/Foundation.h> //框架,是一个核心基础框架,里面提供了很多OC的基础语法类库,左边代表框架,右边代表这个框架里的头文件。 /*例如:在Animal类中,有一个Cry方法,里面打印出 在叫。然而对于不同的动物来说,都有着不同的叫声,狗是 汪汪汪,猫是 喵喵喵等等,所以Animal类中的Cry方法就不通用了,这个时候 我们需要在重写这个方法。 */ /*动物类开始*/ @interface Animal: NSObject{ @public NSString * _name; //...... } -(void) Cry; @end @implementation Animal{ -(void) Cry{ NSLog(@"动物在叫。"); } } @end /*动物类结束*/ /*猫类开始*/ @interface Cat: Animal{ NSString * _name; //...... } -(void) Cry; -(void)setName:(NSString *)name; -(NSString *)name; @end @implementation Cat{ -(void)setName:(NSString *) name{ if([name isEqualTo:@"你麻痹"]){ _name = @"无名"; }else{ //给属性赋值为你传进来的参数 _name = name; } } -(NSString *)name{ //返回这个属性 return _name; } -(void) Cry{ NSLog(@"小猫在喵喵喵的叫。"); } } @end /*猫类结束*/ /*类似于猫类,可以写出狗类、猪类等*/
二、self和super关键字
/* 之前说过,类方法可以调用另一个类方法,语法就是 [类名 方法名]; 那么问题来了,对象方法怎么调用另外一个对象方法?? self: 用途: 1.可以在对象方法里面调用另外一个对象方法。 [self 方法名]; 2.当方法内局部变量和成员变量同名时,用self关键字可以显示的调用成员变量。 语法:self->成员变量名; 之前调用对象方法语法:[对象 方法名]; 调用对象属性: 对象->成员变量; 当使用self的时候,self代表谁呢? 答案: 谁调用这个方法,这个方法里的self就是谁!!! 例:现有Person类和Student类,里面都有sayHi和classTest1方法。 [p sayHi];//那么此时sayHi方法里的self就是p对象 [p2 sayHi];//那么此时sayHi方法里的self就是p2对象 [Person classTest1];//那么此时classTest1方法里的self就是Person类 [Student classTest1];//那么此时classTest1方法里的self就是Student类 super:可以显示的调用父类中的方法。 应用场景:当子类改写父类的时候,其实父类的方法功能还是需要用的,只不过又多增加一些特有的操作,那么就可以在改写的时候先用super关键字调用一下父类的原方法,然后再写需要添加的代码。 super其实不代表谁,就是一个关键字,能够方便的让你调用父类的东西而已。 */ //实例我就 简写了 //Person类 @interface Person:NSObject{ } -(void) sayHi; -(void) Test1; @end @implementation Person{ -(void) sayHi{ NSLog(@"Person哈哈哈"); } -(void) Test1{ [self sayHi]; NSLog(@"Person测试"); } } @end //Student类 @interface Student:Preson{ } -(void) sayHi; -(void) Test1; -(void) Test2; @end @implementation Person{ -(void) sayHi{ NSLog(@"Student哈哈哈"); } -(void) Test1{ [self sayHi]; NSLog(@"Student测试"); } -(void) Test1{ [supersayHi]; NSLog(@"Student测试"); } } @end #import <Foundation/Foundation.h> #import "Person.h" #import "Student.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person p =[Person new]; [p Test1];//Person哈哈哈 Person测试 Student stu =[Studentnew]; [stu Test1];//Student 哈哈哈 Student 测试 [stu Test2];//Person哈哈哈 Student 测试 } return 0; }
还有一种情况,请看代码
//Person类 @interface Person:NSObject{ } -(void) Test2; +(void) Test3; @end @implementation Person{ -(void) Test2{ NSLog(@"self=%@",self); } +(void) Test3{ NSLog(@"self=%@",self); } } @end //Student类 @interface Student:Preson{ } -(void) StuTest2; +(void) StuTest3; @end @implementation Person{ -(void) StuTest2{ [super Test2]; NSLog(@"Student测试"); } +(void) StuTest3{ [super Test3]; NSLog(@"Student测试"); } } @end #import <Foundation/Foundation.h> #import "Person.h" #import "Student.h" int main(int argc, const char * argv[]) { @autoreleasepool { //Student stu =[Studentnew]; //[stu StuTest2];//打印出Student类 [Student StuTest3];//打印出Student类 } return 0; }
三、访问修饰符的介绍和用法
/* @public:所有地方都能访问(注意:是无论哪个地方) @protected(默认): 本类以及子类、子类的子类、子类的子类的子类………………都可以访问 @private:私有的。只能在本类中访问 @package(不会用):介于@public和@private之间的。只能在当前框架中使用,简单来就说就是只能在当前的项目里面用 注意:哪怕是@private的,子类也可以继承到,只是不能用!(不够严谨,以后学到KVC,@private的也可以用) 注意:访问修饰符的修饰范围只能是它定义的位置到下一个访问修饰符 注意:访问修饰符不能修饰方法 “私有”成员变量: 把成员变量,写在@implementation 类名后面的大括号里面。 这种做法可以让子类根本都看不到这个私有成员变量,但是却也继承了,其实只是欺骗了编译器,编译器以为没有,达到“私有”目的。 “私有”方法同上,子类可以继承到,但是不可以直接用。 */ //实例: //写在这里就可以做到私有且子类不可见 @implementation Person{ int _age; }
四、多态
/* 多态:同一种行为,不同的实现 代码中实现多态的步骤: 1.要有继承关系 2.子类重写父类方法 3.用父类类型的指针指向子类的对象 4.然后调用方法 */ //例如: //动物类中的动物叫的方法 -(void) AnimalShout(){ NSLog(@"叫叫叫"); } //猫 -(void) CatShout(){ NSLog(@"喵喵喵"); } //狗 -(void) DogShout(){ NSLog(@"汪汪汪"); } //调用 Animal ani = [Catnew]; [ani CatShout];//猫叫 ani = [Dog new]; [ani DogShout];//狗叫
五、类的本质和SEL类型
/* 类也是存在内存里面的,存在全局区,对象存在堆区。 类既然存在内存里面,它是以什么类型存的呢??? 因为我们说要把数据存在内存里面,都要有相应的数据类型 。 10;用int类型来存。 10.3f; 用float类型来存。 它用的是类类型来存的。类类型就是Class。 Class是一个像int float等等一样的类型。 获得这个类类型的数据: 1.[类名 class] 可以得到 2.[对象 class] 也可以得到 作用: 1. new一个对象 2. 调用类方法 注意:大写的Class是类型 小写的class是方法 类的本质是结构体,类其实也是一个对象,是类类型对象(Class对象); */ #import <Foundation/Foundation.h> @interface Person : NSObject{ @public int _age; } -(void)sayHi; +(void)test; @end @implementation Person -(void)sayHi{ NSLog(@"hello"); } +(void)test{ NSLog(@"类方法调用"); } @end int main(int argc, const char * argv[]) { @autoreleasepool { //Person *p = [Person new]; //此时就得到了Person存在全局区的类数据,所以也就是说现在cp就是我们的Person类 Class cp = [Person class]; Person *p = [Person new];//此时p是Person对象 Class cp2 = [p class];//调用对象class方法 NSLog(@"%p",cp); NSLog(@"%p",cp2); Person *p2 = [cp2 new]; [p2 sayHi]; // NSLog(@"%p",[p valueForKey:@"isa"]); //Class cp相当于写了一个objc_class *cp //怎么用呢??? //平时类可以创建对象,那么我们可不可以用我们刚刚拿到的Class类型的数据来创建对象呢?? /* Person *p = [cp new]; //等于 Person *p = [Person new]; p->_age = 20; [p sayHi]; Person *p2 = [Person new]; p2->_age = 20; [p2 sayHi]; */ // [Person test]; // [cp test]; } return 0; }
上面简单的介绍了类的本质,还有一个SEL类型,请看下面:
/* 方法也是要存储。 方法的类型: SEL类型 其实类里面(Class对象)里面没有存方法的实现,而是存了一个方法的地址,这个地址就是SEL类型。 方法的实现存哪去了??方法的本质是函数,存在代码区。 我们每次调用方法,其实是把方法包装成了SEL类型,然后去类类型的对象找到它所存的方法列表,进行比对,如果比对成功,则执行,如果所有的列表(包括父类,直到NSObject)都比对完,还没有相等的就报错。 那么我们自己怎么获得方法的SEL类型? 通过 @selector(方法名); 这个就可以得到SEL类型 */ #import <Foundation/Foundation.h> @interface Person : NSObject -(void)sayHi; +(void)clsTest; @end @implementation Person -(void)sayHi{ NSLog(@"哈哈哈哈哈"); } +(void)clsTest{ NSLog(@"我是类方法"); } @end int main(int argc, const char * argv[]) { @autoreleasepool { //怎么用SEL类型调用方法???? //因为我们包装的是对象方法,所以先要有对象 Person *p = [Person new]; // //得到SEL类型了 // SEL funcSay = @selector(sayHi); // // [p performSelector:funcSay]; [p performSelector:@selector(sayHi)]; //这句和上面两句一样 //类方法应用类名调用 [Person performSelector:@selector(clsTest)]; } return 0; }