《Objective-c》-(三大特性:封装、继承、多态)

 

一、封装(set方法和get方法)

  • 封装的好处:

 过滤掉不合理的值,屏蔽内部的赋值细节,让外界不比关心内部的细节。
  • set方法:

 1.作用:提供一个方法给外界设置成员变量的值
 2.命名规范:
   1> 方法名必须是set开头
   2> set后面跟上成员变量的名称,并且成员变量的首字母必须是大写
   3> 返回值一定是void
   4> 一定要接受一个参数,而且参数类型跟成员变量的类型一致
   5> 形参名称不能和成员变量名一样

 

例:

- (void)setAge : (int) newAge; // 方法声明

- (void)setAge : (int) newAge // 方法实现
{
  if (newAge <= 0)
{
  newAge = 1;  // 对传入的参数进行过滤 
}  
age = newAge;
}
  •  get方法:

 1.作用:返回对象内部的成员变量值
 2.命名规范:
   1> 肯定有返回值,并且返回值类型肯定和成员变量类型一致
   2> 方法名和成员变量名一样
   3> 不需要接受任何参数

例:

- (int) age; // 方法的声明

- (int) age // 方法的实现
{
  return age;  
}
  • 成员变量的命名规范

成员变量的命名规范:一定要以下划线“_”开头;

作用:1.让成员变量和get方法的名称区分开

         2.可以跟局部变量区分开,一看到下划线开头的变量,一般都是成员变量

代码练习:

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
    // 成员变量尽量不要用@public保证数据的安全性,没有@public就不能通过   对象->成员变量   来赋值(访问),这是可以提供一个set方法给外界设置成员变量的值(虽然没有了@public,但是在对象方法内还是能直接访问成员变量的)
    // @public
    int age;
}

- (void)setAge:(int)newAge;

- (int)age;

- (void)study;
@end

@implementation Student

// set方法的实现
- (void)setAge:(int)newAge
{
    // 对传进来的参数进行相应的过滤
    if(newAge <= 0)
    {
        newAge = 1;
    }
    
    age = newAge;
}

// get方法的实现
- (int)age
{
    return age;
}

- (void)study
{
    NSLog(@"%d岁的学生在学习", age); // 这里的使用age即使在声明成员变量时没有写@public也可以直接访问(对象方法内部可以直接访问成员变量)
}
@end

int main()
{
    Student *stu = [Student new];
    
    // 通过调用set方法对对象内部的成员变量进行赋值
    [stu setAge:-10];
    
    [stu study];
    
    // 由于没有了@public这是调用get方法获取对象内部的成员变量值
    NSLog(@"这个学生的年龄是%d", [stu age]);
    
    return 0;
}

 

二、继承

1.继承的好处:

 

  • 抽取重复代码

  • 建立了类与类之间的关系

  • 子类拥有了父类中的所有成员变量和方法

2.继承的坏处:

  • 耦合性太强,子类与父类必须相互依存(如果将父类的代码 删除了,子类也不能正常使用);

3.继承注意点:

  • 基本所有类的根类是NSObject;

  • 父类的声明必须写在子类前面,实现无所谓;

  • 不允许子类有和父类相同的成员变量;

  • 调用某个方法时,优先在当前类中找,找不到就会去父类中找(内存分析见下图);

  • 允许子类中有和父类相同的方法,即:重写(子类重新实现父类中的某个方法,这是在子类中调用该方法就会优先执行子类中重新实现的方法,覆盖父类以前的方法)

 

 4.继承的使用场合:

  • 当两个类拥有相同属性和方法时,就可以将相同的东西抽取到一个父类中;

  • 当A类中完全拥有B类中的部分属性和方法时,可以考虑让B类继承A类;

  • 注意:1.并不是只要有相同的属性和方法就可以使用继承;

                2.使用继承时要考虑这两个类是否存在逻辑上的关系;

                3.如果不存在逻辑关系,就用组合

 代码一:

#import <Foundation/Foundation.h>

/*********Animal类的声明**********/
@interface Animal : NSObject
{
    int _age;
    double _weight;
}

- (void)setAge:(int)age;
- (int)age;
@end

/*********Animal类的实现**********/
@implementation Animal
- (void)setAge:(int)age
{
    _age = age;
}
- (int)age
{
    return _age;
}
@end
// : Animal 代表Dog类继承了Animal类,那么Dog类中就拥有了Animal类中的所有成员变量及方法
// Animal类就是Dog类的父类
// Dog类就是Animal的子类

/*********Dog类的声明**********/
@interface Dog : Animal

@end

/*********Dog类的实现**********/
@implementation Dog

@end

int main()
{
    Dog *d = [Dog new];
    
    [d setAge:10];
    
    NSLog(@"age=%d", [d age]);
    
    return 0;
}

 代码二:

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    int _age;
}

- (void)run;
@end

@implementation Person
- (void)run
{
    NSLog(@"person---跑");
}
@end

// 注意点1:子类的声明必须写在父类的声明后面
@interface Student :Person
{
    int _no;
// 注意点2:不允许在子类中有和父类相同的成员变量
    // int _age; // 报错:重复定义_age(error: duplicate member '_age')
}

// - (void)run; // 这里可以不写,因为在其父类中已经有声明,如果子类实现中有该方法的实现,就是重写(子类重新实现父类中的某个方法)
@end

@implementation Student
// 重写:子类重新实现父类中的某个方法,这是在子类中调用该方法就会优先执行子类中重新实现的方法,覆盖父类以前的方法
- (void)run
{
    NSLog(@"student---跑");
}
@end

int main()
{
    Student *s = [Student new];
    
    [s run]; // 会现在子类中找run方法,找到了就优先执行当前类中run方法,没有找到就会去其父类中找
    
    return 0;
}

 

代码三:

#import <Foundation/Foundation.h>

@interface Score : NSObject
{
    int _cScore;
    int _ocScore;
}
@end

@implementation Score

@end

// 继承:xx 是 xxx
// 组合:xx 拥有 xxx

@interface Student :NSObject
{
    int _no;
    int _age;
    Score *_score; // 这里用的就是组合
}

@end

@implementation Student

@end

int main()
{
    
    return 0;
}

 

 三、多态(父类指针指向子类对象)

1.代码体现:父类类型的指针指向子类对象;

2.好处:如果函数、方法形参中使用的是父类类型,就可以传入父类、子类对象;

3.局限性:父类类型的变量不能直接调用子类特有的方法,必须强转为子类类型后才能直接调用子类特有方法

 

 

 4.代码练习:

#import <Foundation/Foundation.h>

@interface Animal : NSObject
- (void)eat;
@end

@implementation Animal
- (void)eat
{
    NSLog(@"Animal---吃东西");
}
@end


@interface Dog : Animal
- (void)run;
@end

@implementation Dog
- (void)eat
{
    NSLog(@"Dog---吃东西");
}
- (void)run
{
    NSLog(@"狗跑起来了");
}
@end

@interface Cat : Animal

@end

@implementation Cat
- (void)eat
{
    NSLog(@"Cat---吃东西");
}
@end

// 喂狗
void feed(Dog *d)
{
    [d eat];
}

// 喂猫
void feed2(Cat *c)
{
    [c eat];
}

// 由于喂狗和喂猫的代码基本一致,所以考虑将其抽取到一个函数内
// 多态的好处:如果形参中使用的是父类类型,就可以传入父类、子类对象
void feed3(Animal *a)
{
    [a eat];
}

int main()
{
    /***************1.多态的基本使用******************/
    
    // 多态:父类指针指向子类对象
    Animal *a = [Dog new];
    
    // 调用方法时会动态检测对象的真实类型,这里对象a的真实类型是Dog
    [a eat]; // 输出:Dog---吃东西
    
    
    /***************2.OC的弱语法******************/
    
    /* 注意点:
     OC的弱语法:这里仅仅是一个警告(warning: incompatible pointer types initializing 'Dog *' with an expression of type 'Animal *' [-Wincompatible-pointer-types])
     */
    // Dog *d = [Animal new]; // 虽然仅仅是一个警告,但是在意思上不合理(动物是狗)
    
    // [d eat]; // 输出:Animal---吃东西
    
    
    /***************3.多态的好处******************/
    // 多态的好处:如果形参中使用的是父类类型,就可以传入父类、子类对象
    
    Dog *d2 = [Dog new];
    feed(d2);
    
    Cat *c2 = [Cat new];
    feed2(c2);
    
    Dog *d3 = [Dog new];
    feed3(d3);
    
    Cat *c3 = [Cat new];
    feed3(c3);
    
    /***************4.多态的局限性******************/
    
    Dog *dd = [Dog new]; // Dog类型
    
    Animal *aa = [Dog new]; // Animal类型
    
    // OC的弱语法: warning: 'Animal' may not respond to 'run'
    // 对于编译器来讲,编译器认为aa的类型是Animal,这里[aa run]就会去Animal中找是否有run的具体实现,没有找到就会警告,但是
还是会运行成功,因为在运行是会动态检测aa的真实类型(Dog),Dog类中有run方法的具体实现,就能正常运行
// 多态的局限性:父类类型的变量不能用来调用子类特有的方法 //
[aa run]; // 对于上面的问题,可以将aa强制转换成Dog类型,编译器就不会警告 Dog *dd = (Dog *)aa; // 将aa强制转换成Dog *类型 [dd run]; return 0; }

 

 
 
 
 
 
posted @ 2015-03-08 23:39  深秋的露水  阅读(695)  评论(0编辑  收藏  举报