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 ;
}

还有一种情况,请看代码

//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 ;
}

三、访问修饰符的介绍和用法

/*
@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 ;
}

上面简单的介绍了类的本质,还有一个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 ;
}
上一篇:10分钟了解代码命名规范(Java、Python)


下一篇:java编程之常见的排序算法