学 Objective-C 时总被 “继承”“多态” 这些词绕晕?写代码时重复定义相同的属性和方法,感觉特别麻烦?看到别人用几行代码实现复杂功能,自己却不知道如何简化?不少新手朋友跟我吐槽,面向对象的这些概念听着抽象,实际用起来更是摸不着头绪。其实啊,继承和多态是让代码变简洁、变灵活的 “神器”,就像盖房子时用现成的框架改造成不同户型,能省超多功夫。今天兔子哥就用大白话讲透继承与多态的用法,附上个动物类的实战案例,代码一步步解析,新手跟着练,很快就能学会用面向对象思想写代码了。
一、先搞懂:继承到底能帮我们省多少事?生活例子讲明白
Q:“继承听着挺玄乎,它到底能解决啥问题?”
A:简单说,继承就是 “少写重复代码”。比如狗和猫都属于动物,都有名字、年龄,都能吃东西;但狗会汪汪叫,猫会喵喵叫。如果分别定义 Dog 类和 Cat 类,就得重复写名字、年龄这些相同的属性和方法。用继承的话,定义一个 Animal 父类,把相同的属性方法放进去,Dog 和 Cat 直接 “继承” 过来,只写自己特有的部分就行,多省事!
继承的核心优势(新手必看)
| 场景 | 不使用继承 | 使用继承 |
|---|---|---|
| 代码量 | Dog 和 Cat 类重复定义 name、age 属性和 eat 方法 | Animal 类定义一次,Dog 和 Cat 直接继承,只写特有方法 |
| 维护性 | 改 name 属性时,Dog 和 Cat 类都要改 | 只需改 Animal 类,所有子类自动生效 |
| 扩展性 | 新增 Animal 子类(如 Bird),需重新写所有基础属性 | 直接继承 Animal,只需加 fly 等特有方法 |
你看,继承就像 “站在巨人的肩膀上”,不用重复造轮子,这也是面向对象编程的核心优势之一。
二、继承的实现:Objective-C 的 “规矩”,子类如何 “继承” 父类?
Objective-C 中实现继承很简单,定义子类时在类名后加 “: 父类名” 就行。咱用动物例子实战,先定义 Animal 父类,再让 Dog 和 Cat 继承它。
1. 父类 Animal:定义公共属性和方法
先写父类的头文件
Animal.h,放所有动物都有的属性(name、age)和方法(eat):objective-c
// Animal.h#import @interface Animal : NSObject@property (nonatomic, copy) NSString *name; // 名字(公共属性)@property (nonatomic, assign) int age; // 年龄(公共属性)- (void)eat; // 吃东西(公共方法)@end 再写实现文件
Animal.m,实现 eat 方法:objective-c
// Animal.m#import "Animal.h"@implementation Animal- (void)eat {NSLog(@"%@在吃东西~", self.name); // 所有动物吃东西的通用描述}@end2. 子类 Dog:继承父类,添加特有方法
Dog 类继承 Animal,只需写它特有的方法(bark 叫)。头文件
Dog.h:objective-c
// Dog.h#import "Animal.h" // 必须导入父类头文件@interface Dog : Animal // 继承Animal- (void)bark; // 狗特有方法:叫@end实现文件
Dog.m:objective-c
// Dog.m#import "Dog.h"@implementation Dog// 继承的eat方法可以直接用,也能重写(后面讲)- (void)bark {NSLog(@"%@汪汪叫:旺~旺~", self.name); // 狗叫的特有实现}@end3. 子类 Cat:同样继承父类,添加特有方法
Cat 类和 Dog 类似,头文件
Cat.h:objective-c
// Cat.h#import "Animal.h"@interface Cat : Animal // 继承Animal- (void)meow; // 猫特有方法:叫@end实现文件
Cat.m:objective-c
// Cat.m#import "Cat.h"@implementation Cat- (void)meow {NSLog(@"%@喵喵叫:喵~喵~", self.name); // 猫叫的特有实现}@end4. 测试继承效果:子类能直接用父类的属性和方法
在 main 函数里创建 Dog 和 Cat 对象,试试它们是否能使用父类的 name、age 和 eat 方法:
objective-c
#import "Dog.h"#import "Cat.h"int main(int argc, const char * argv[]) {@autoreleasepool {// 创建Dog对象Dog *dog = [[Dog alloc] init];dog.name = @"旺财"; // 使用继承的name属性dog.age = 3; // 使用继承的age属性[dog eat]; // 调用继承的eat方法[dog bark]; // 调用自己的bark方法// 创建Cat对象Cat *cat = [[Cat alloc] init];cat.name = @"咪宝";cat.age = 2;[cat eat];[cat meow];}return 0;}运行结果会输出:
plaintext
旺财在吃东西~旺财汪汪叫:旺~旺~咪宝在吃东西~咪宝喵喵叫:喵~喵~你看,子类不用自己定义 name、age 和 eat,直接能用父类的,这就是继承的好处!
三、方法重写:子类不想用父类的方法?自己改!
有时候父类的方法不符合子类需求,比如 Animal 的 eat 方法太通用,想让 Dog 吃东西时更具体(比如 “吃骨头”),这时候可以重写父类方法。
重写的实现:子类中重新定义父类的方法
在 Dog 类的实现文件里重写 eat 方法:
objective-c
// Dog.m(添加重写的eat方法)@implementation Dog// 重写父类的eat方法- (void)eat {NSLog(@"%@在啃骨头,咔嚓咔嚓~", self.name); // 狗吃东西的具体实现}- (void)bark {NSLog(@"%@汪汪叫:旺~旺~", self.name);}@end这时候再运行
[dog eat],会输出 “旺财在啃骨头,咔嚓咔嚓~”,而不是父类的通用描述。Q:“重写时想保留父类的方法逻辑,再加点自己的内容咋办?”
A:用
super调用父类方法!比如让 Dog 先执行父类的 eat,再加自己的内容:objective-c
- (void)eat {[super eat]; // 调用父类的eat方法NSLog(@"%@吃完骨头摇尾巴~", self.name); // 新增自己的逻辑}运行后会先输出父类的 “旺财在吃东西~”,再输出新增的内容,灵活吧?
四、多态:父类指针指向子类对象,一行代码调用不同实现
多态是继承的 “高级用法”,简单说就是 “父类类型的变量可以指向子类对象,调用方法时会执行子类的实现”。听起来绕,实际用起来超方便。
多态的实战:用 Animal 指针统一管理 Dog 和 Cat
在 main 函数里用 Animal 指针创建对象,调用 eat 方法:
objective-c
int main(int argc, const char * argv[]) {@autoreleasepool {// 父类指针指向Dog对象Animal *animal1 = [[Dog alloc] init];animal1.name = @"旺财";[animal1 eat]; // 会执行Dog重写的eat方法// 父类指针指向Cat对象Animal *animal2 = [[Cat alloc] init];animal2.name = @"咪宝";[animal2 eat]; // 会执行Cat的eat方法(如果Cat也重写了的话)}return 0;}如果 Cat 也重写了 eat 方法:
objective-c
// Cat.m重写eat- (void)eat {NSLog(@"%@在吃小鱼干,吧唧吧唧~", self.name);}运行结果会输出:
plaintext
旺财在啃骨头,咔嚓咔嚓~咪宝在吃小鱼干,吧唧吧唧~你看,同样是
[animal eat],根据指向的子类对象不同,执行的方法也不同,这就是多态的魅力!用多态能写出超级灵活的代码,比如定义一个数组存放各种动物,循环调用它们的 eat 方法,不用管具体是狗还是猫。五、避坑指南:新手用继承和多态常踩的 3 个坑
1. 继承层次别太深,不然代码会变乱
有的新手觉得继承好用,就搞出 “Animal→Mammal→Dog→PetDog” 这样的多层继承,层级越深,代码越难维护。建议继承层次不超过 3 层,够用就行。
2. 重写方法时别漏写参数或返回值
重写父类方法必须和父类的方法名、参数、返回值完全一致,不然会被当成新方法,不会触发多态。比如父类是
- (void)eat,子类写成- (void)eat:(int)food,这就不是重写了。3. 多态调用子类特有方法会报错
父类指针不能直接调用子类的特有方法,比如
animal1是 Animal 指针,指向 Dog 对象,调用[animal1 bark]会报错,因为 Animal 类里没有 bark 方法。必须转成子类类型才能调用:[(Dog *)animal1 bark],但尽量少这么做,破坏多态的灵活性。六、兔子哥的实战建议:这样练习继承和多态更有效
- 从生活场景找例子:除了动物,还可以写 “Shape→Circle→Rectangle”(图形→圆形→矩形),父类有计算面积的方法,子类重写具体实现,多练几个场景就熟了。
- 用多态简化代码:写一个统一的处理函数,接收父类类型参数,比如
- (void)feedAnimal:(Animal *)animal,里面调用[animal eat],这样不管传 Dog 还是 Cat 都能处理,体会多态的便捷。 - 先模仿再修改:刚开始可以照抄教程的代码,运行成功后试着改改,比如给 Animal 加个 “sleep” 方法,让子类重写,看看多态是否生效。
兔子哥觉得,继承和多态是 Objective-C 面向对象的 “灵魂”,学会它们,代码会从 “一堆重复逻辑” 变成 “清晰的层次结构”。刚开始可能觉得抽象,但用生活例子类比,再结合代码实战,很快就能理解。
其实面向对象的核心就是 “抽象和复用”,继承解决复用问题,多态解决灵活调用问题。新手不用追求一次完美,先写出能运行的继承关系,再尝试重写方法,最后用多态优化代码,一步步来。之前有个学员用这个动物案例练习,一周后就能自己设计类的继承关系了,进步特别快。
现在就打开 Xcode,试试写今天的 Animal、Dog、Cat 类,感受一下继承如何省代码,多态如何让调用更灵活,你会发现面向对象编程真的很有意思!
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~