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

 

posted @ 2015-11-26 23:33  祁临芯  阅读(372)  评论(0编辑  收藏  举报
友情链接:初心商城