Objective-C中的继承与多态, Category, Extension

2023-11-13

1、继承与多态
先要理解实例变量的作用域:
这里写图片描述
再看继承:

这里写图片描述

即:子类的方法和属性 = 从父类继承得到的方法和属性 + 子类新增的方法和属性

例子:

//  Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject    //其父类是NSObject
{
    NSString *_name;            //实例变量默认是@protected的
    @private
    int _age;                   //父类的私有实例变量
}
-(void)run;                     //声明run方法
@end
//  Person.m
#import "Person.h"
float _salary;                  //定义一个父类的隐藏实例变量

@implementation Person
-(void)run{                     //实现run方法
    NSLog(@"person run!");
}
@end

//  Student.h  
#import "Person.h"
@interface Student : Person     //Student类继承于Person
-(void)printStu;
@end
//  Student.m
#import "Student.h"
@implementation Student
-(void)printStu{
    NSLog(@"student run!");
    NSLog(@"name: %@", _name);  //使用继承自父类的实例变量
}
-(void)work{
    NSLog(@"s w");
}
@end

注意:
(1)父类的私有实例变量虽然能被子类继承,但不能被子类使用。如在Student类的printStu方法使用 Person类的private属性 _age则报错:

这里写图片描述
(2)父类的隐藏实例变量(在 .m文件定义的,也有的地方叫作私有变量,但觉得跟 private 的属性搞混)不能被子类继承。如如在Student类的printStu方法使用 Person类的隐藏实例变量_salary则报错:
这里写图片描述
(3)OC中的继承是单继承。即一个类至多只能有一个父类。
(4)子类可以重写父类的方法。当子类对从父类继承得到方法不满意时,可以改写之,只需要在子类的 .m文件定义一个返回类型、方法名、参数都与父类原方法相同的方法。如上例中父类Person类已经定义了run方法,Student类可以在其.m文件中添加定义:

-(void)run{                 //重写run方法,覆盖父类的run方法
    NSLog(@"student run!");
}

调用时,父类的对象调用父类的方法,子类的对象调用子类的方法,不引起冲突:

//  main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];      //定义一个父类Person的对象
        Student *student = [Student new];   //定义一个子类Student的对象
        [person run];                       //父类的对象调用父类的run方法
        [student run];                      //子类的对象调用子类的run方法
    }
    return 0;
}

//运行结果:

这里写图片描述
(5)继承体系中方法调用寻找顺序:
当前类 —(没找到)—> 父类 —(没找到)—> 父类的父类 —(没找到)—> … —-(没找到)–> NSObject —(没找到)—> 报错

多态

使用多态的条件:(1)有继承;(2)有方法的重写;(3)父类指针指向子类对象
如对于上面例子,Student类继承了Person类,现在再创建一个Teacher类也继承Person类,然后两个子类都重写了run方法:

//  Teacher.h
#import "Person.h"
@interface Teacher : Person     //继承了Person
@end
//  Teacher.m
#import "Teacher.h"
@implementation Teacher
-(void)run{                     //重写run方法,覆盖父类的run方法
    NSLog(@"teacher run!");
}
@end
//  main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
#import "Person+doSomething.h"
#import "Teacher.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *person = nil;       //定义一个父类Person的对象
        person = [Student new];     //此时person指针指向一个子类Student的对象
        [person run];               //调用的是Student中的run方法

        person = [Teacher new];     //此时person指针指向一个子类Teacher的对象
        [person run];               //调用的是Teacher中的run方法
    }
    return 0;
}

多态的原理:
动态绑定:动态类型能使程序直到运行时才确定对象所属的类型。动态类型绑定能使程序直到运行时才确定要对象调用的方法。
注意:
(1)存在多态时,父类可以调用子类特有的方法,但要经过强制类型转换
这里写图片描述

这里写图片描述

(2)不存在多态时,父类不可以调用子类特有的方法,即使是强制类型转换也不行:
这里写图片描述

2、Category

OC特有,被翻译为:分类、类别、类目
作用:在不修改原有的类、不需要原有的类的代码的基础上增加新的方法(也只能是方法,不能增加实例变量)。

使用条件及好处:
(1)在分模块开发一个庞大的类时,有利于分工合作。
(2)替代子类的继承。因为继承可能影响原有的继承系统,而且只能单继承。而一个类可以有多个Category。
(3)一个Category中可以有多个方法,所以可以利用它来将方法归类,使得更好地更新和维护。

使用方法:声明 ——> 实现 ——> 使用。
如给上面例子的Person类新增Category:

//  Person+doSomething.h            注意文件名为“原类名+Category名”
#import "Person.h"
@interface Person (doSomething)     //Person为原有的类,doSomething为Category名
-(void)work;                        //声明方法
@end

//  Person+doSomething.m
#import "Person+doSomething.h"
@implementation Person (doSomething) //形式与.h文件一样
-(void)work{                         //实现方法
    NSLog(@"person work");
}
@end
//  main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+doSomething.h"       //导入声明文件
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *person = [Person new];
        //person可以像使用自身拥有的其他方法一样地调用Category为Person类新增的方法
        [person work];
    }
    return 0;
}

注意:
(1)category只能给类增加方法,不能增加成员变量、@property(可能编译不报错,但运行有问题)。如:

//  Person+doSomething.h
#import "Person.h"
@interface Person (doSomething)
@property int _height;          //试图在category增加成员变量
-(void)work;
@end

这里写图片描述
(2)category 的 @implementation……@end 可以单独放在一个 .m文件,也可以放在对应的 .h文件中 @implementation……@end 之后。也可以两者都放在原有类的 .h文件的 @implementation……@end之后,直接声明、实现category。

(3)category可以使用原有类的私有实例变量,但原有类的隐藏实例变量仍对category不可见。如:

这里写图片描述
(4)当有多个分类和原有类都有同名方法时,原有类的方法被覆盖,而且执行最后编译的category文件的方法。如

//  Person.m
#import "Person.h"
@implementation Person
-(void)run{            //在原有的Person类中实现run方法
    NSLog(@"person run!");
}
@end

//  Person+doSomething.m
#import "Person+doSomething.h"
@implementation Person (doSomething)
-(void)run{            //在第一个category doSomething中实现run方法
    NSLog(@"person run at doSomething!");
}
@end

//  Person+play.m
#import "Person+play.h"
@implementation Person (play)
-(void)run{             //在第二个category play中实现run方法
    NSLog(@"person run at play!");
}
@end

//  main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+doSomething.h"      
#import "Person+play.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person run];   //在主函数中调用run方法
    }
    return 0;
}
//输出结果:person run at doSomething!

因为在编译文件中,Person+doSomething.m 排在最后:
这里写图片描述
所以在 category 中是可以重写原有类的方法的,但系统会报警告:
这里写图片描述
而且也不推荐在 category 中重写原有类的方法,因为:
(1)一旦在category中重写了方法,则原有类的同名方法被覆盖掉,再也不能访问,所以也不能像子类那样发送消息给super就可以调用父类的同名方法,所以不能在原有方法的基础上增加功能,除非你想重复写那些功能。
(2)从上面的例子我们可以知道当多个类拥有同名的方法时,调用这个方法的结果貌似不是我们能控制的。
(3)在category中重写的方法不仅影响到原有的类,而且会影响原有类的子类,因为那些子类很可能继承并使用了这个方法,后果不言而喻。

了解了category的应用和优缺点,我们再来看下它的原理:
在Objective-C Runtime Reference中的简介:

Category
An opaque type that represents a category.

即Category是一种类型,这个类型代表了一个分类。
而在runtime.h中的定义:

typedef struct objc_category *Category;

所以Category是指向一个objc_category结构体的指针。而objc_category结构体的声明为:

struct objc_category {
    char *category_name                                      OBJC2_UNAVAILABLE;
    char *class_name                                         OBJC2_UNAVAILABLE;
    struct objc_method_list *instance_methods                OBJC2_UNAVAILABLE;
    struct objc_method_list *class_methods                   OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

分别存储了指向分类名、原有类的名称、对象方法列表的的结构体、类方法列表的结构体、遵守的协议列表的结构体的五个指针。
其中结构体objc_method_list定义如下:

struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;

    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}  

而我们定义的方法:

Method
An opaque type that represents a method in a class definition.

即也是一个类型,这个类型代表了在类定义中的一个方法。

在runtime.h中的定义:

typedef struct objc_method *Method;

所以Method是指向一个objc_method 结构体的指针。而 objc_method 结构体的声明为:

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}  

所以一个 objc_method 结构体存储了单个方法的方法名、方法类型、实现方法的地址。其中IMP是“implementation”的缩写,定义和描述如下:

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES

typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id (*IMP)(id, SEL, ...); 
#endif

Discussion
This data type is a pointer to the start of the function that implements the method. This function uses standard C calling conventions as implemented for the current CPU architecture. The first argument is a pointer to self (that is, the memory for the particular instance of this class, or, for a class method, a pointer to the metaclass). The second argument is the method selector. The method arguments follow.
其中:

#define OBJC_OLD_DISPATCH_PROTOTYPES 1

对常量OBJC_OLD_DISPATCH_PROTOTYPES的解释:

Dispatch Function Prototypes
This macro indicates whether dispatch functions must be cast to an appropriate function pointer type.

亦即IMP是一个指针,指向了方法实现的函数的首地址,即类似C语言中的函数指针。这个实现函数有三个参数,第一个参数是对象自己(self),第二个是参数是方法选择器SEL,之后的参数是方法的参数。

所以,和调用类的方法一样,要调用category中的方法,也是要将方法名包装成SEL,然后在category中找含有方法数组列表的结构体,然后找单个方法,寻找的方法是将包装的SEL与每个方法的SEL型名称对比,两者一致为找到方法,然后通过IMP型指针找到实现方法的函数,调用该函数,即可完成调用category的方法。

(3)非正式协议(informal protocol)

是个特殊的category,特殊在于非正式协议是给根类 (root class,如 NSObject)增加方法。非正式协议一般不需要进行实现,一般在子类中进行方法的重写。

3、Extension

也是个特殊的category,特殊在与它没有名字!所以也叫匿名分类。
而它不仅可以给原有类增加方法,还可以增加成员变量。
如:

//  Person_study.h                 extension的头文件,名字为“原有类名_extension名”
#import "Person.h"
@interface Person ()               //括号内没有名字
{
    @private
    int studyTime;                 //在extension中增加了成员变量
}
-(void)studyHard;                  //在extension中增加了study方法
@end

//  Person.m
#import "Person.h"
#import "Person_study.h"           //要导入extension的.h文件才能使用增加的成员变量
@implementation Person
-(void)studyHard{                               //在原有类的.m文件中实现在extension study中声明的方法
    NSLog(@"person study at %d", studyTime);    //可以使用extension study中增加的成员变量
}
@end

//  main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person_study.h"            //导入声明文件
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person studyHard];         //调用在extension中增加的方法
    }
    return 0;
}

最后对比一下:
这里写图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Objective-C中的继承与多态, Category, Extension 的相关文章

  • Windows下的Memcache安装

    很多phper不知道如何在Windows下搭建Memcache的开发调试环境 最近个人也在研究Memcache 记录下自己安装搭建的过程 其实我开始研究Memcache的时候并不知道居然还有memcached for Win32这个鸟东西
  • Java知识点汇总--多态

    Java多态 1 多态 1 1 多态的概述 1 2 多态中的成员访问特点 1 3 多态的好处和弊端 1 4 多态中的转型 1 5 多态的案例 1 多态 1 1 多态的概述 什么是多态 同一个对象 在不同时刻表现出来的不同形态 多态的前提 要
  • C++ 继承详解

    C 继承 继承语法 继承方式 private继承特点 改变访问权限 名字遮蔽 继承时的对象模型 无变量遮蔽 有变量遮蔽 final关键字 继承语法 继承的一般语法为 class 派生类名 继承方式 基类名 派生类新增加的成员 继承方式 继承
  • 《clean architecture》第二部分编程范式读书笔记

    前言 第二部分的主题内容 Chap3 编程范式总览 三个编程范式 思考和小结 Chap4 结构化编程 可推导性 goto 是有害的 功能分解 测试 小结 Chap5 面向对象编程 封装 继承 多态 依赖反转 小结 Chap6 函数式编程 不
  • 设计模式之(二)---代理模式Proxy Pattern

    什么是代理模式呢 我很忙 忙的没空理你 那你要找我呢就先找我的代理人吧 那代理人总要知道 被代理人能做哪些事情不能做哪些事情吧 那就是两个人具备同一个接口 代理人虽然不能干活 但是被 代理的人能干活呀 比如西门庆找潘金莲 那潘金莲不好意思答
  • 多重继承和多继承, super, __mro__

    继承 父类派生子类 子类继承父类 通过继承 可以让子类去拥有父类中的属性和方法 而不必重新编写相同的代码 并且可以在父类的基础上添加新的属性和功能 在继承的同时 子类还可以重写父类中的方法 从而获取与父类不同的功能 实现多态 在 Pytho
  • MMX及SSE优化--SSE篇

    上回讲到针对整数运算的MMX优化技术 然而真正大运算量的图形和声音处理大都用的是浮点运算 而且现在对浮点运算的要求也是越来越高 在这样一个条件下INTEL终于在Pentium III处理中增加针对浮点运算优化的SSE指令 所以所有用过SSE
  • Geometry Shader 概念和实例

    前言 Shader Model 4给我们带来了Geometry Shader这个玩意儿 其实这个东西早就在一些3D动画制作软件中存在了 比如Maya 8 我参考了以前DX10的哪一篇Preview与Csustan edu的一篇比较详尽的教材
  • 【JavaSE系列】第八话 —— 继承和它的边角料们

    导航小助手 思维导图 一 引出继承 二 继承的概念 三 继承的语法 四 父类成员访问 4 1 子类中访问父类的成员变量 4 2 子类访问父类的成员方法 五 super 关键字 5 1 super 成员变量 5 2 super 成员方法 5
  • js声明函数(function)和变量(variable)不得不防的坑

    在工作中初级程序员容易忽略的细节就是函数和变量的声明都存在提升 而且他们之间的提升是不同的 请看下面的演示 function funa console log funa 函数声明 function funb console log funb
  • USB转串口驱动代码分析

    1 USB插入时 创建设备 DriverObject gt DriverExtension gt AddDevice USB2COM PnPAddDevice 步一 调用USB2COM CreateDeviceObject创建功能设备对象
  • Objective-C中的继承与多态, Category, Extension

    1 继承与多态 先要理解实例变量的作用域 再看继承 即 子类的方法和属性 从父类继承得到的方法和属性 子类新增的方法和属性 例子 Person h import
  • android AndroidManifest的category

    CATEGORY ALTERNATIVE 设置这个activity是否可以被认为是用户正在浏览的数据的一个可选择的action CATEGORY APP BROWSER 和ACTION MAIN一起使用 用来启动浏览器应用程序 CATEGO
  • 【C++ 进阶】继承

    一 继承的定义格式 基类又叫父类 派生类又叫子类 二 继承方式 继承方式分为三种 1 public继承 2 protected继承 3 private继承 基类成员与继承方式的关系共有9种 见下表 虽然说是有9种 但其实最常用的还是红框里的
  • 多态原理探究

    概念 当类中声明虚函数时 编译器会在类中生成一个虚函数表 虚函数表是一个存储类成员函数指针的数据结构 虚函数表是由编译器自动生成与维护的 virtual成员函数会被编译器放入虚函数表中 当存在虚函数时 每个对象中都有一个指向虚函数表的指针
  • 老司机带你入门Java基础概念

    因为学习所以收获 因为收获所以不寂寞 请关注 源码猎人 目录 Java简介 Java特性 Java环境概述 Java工作原理 面向对象 对象 类 方法 继承 封装 多态 变量 常见面试题 Java简介 Java是一门面向对象编程语言 Jav
  • 简述多线程的作用以及什么地方用到多线程

    1 多线程的作用 可以解决负载均衡问题 充分利用CPU的资源 为了提高Cpu的使用 采用多线程的方法去同时完成几件事情而互不干扰 2 大多数的情况下 使用多线程 主要是需要处理大量的IO操作或处理的情况需要花大量的时间等 3 iOS 有三种
  • 友元成员函数使用时的注意事项

    友元成员函数的注意事项 友元的概念 友元 的概念其实是针对于类的私有成员来说的 一个类的由于封装的缘故 类体中私有成员是不可以被外界访问的 无论是继承也好 都是无法访问类内私有成员的 但是正是有那么一些人 愿意打破陈规破例访问类体内的私有成
  • C#创建一个收费接口Charge,其中有一个方法charge();创建另一个收费接口Play,其中有一个方法play()。声明类Bus来实现接口Charge,对于Bus中的charge()实现为输出“

    创建一个收费接口Charge 其中有一个方法charge 创建另一个收费接口Play 其中有一个方法play 声明类Bus来实现接口Charge 对于Bus中的charge 实现为输出 公共汽车 1元 张 不计公里数 声明类Taxi来实现接
  • Java学习13:面向对象-多态(Polymorphism)内存分析图解

    1 概述 多态是Java面向对象三大特征之一 多态 Polymorphism 顾名思义 即对象具有多种形态 具体而言 是编译时类型 运行时类型 编译时类型 由声明时的类型决定 一般是父类 运行时类型 由实际对应的对象类型决定 具体是哪个子类

随机推荐

  • Eclipse导入外部工程运行报错:The selection cannot be run on any server

    一 错误描述 课下复习时导入老师的Demo 发现项目报错 在项目上有红色叉 运行时报如下错误 二 解决步骤 1 在项目上右键 gt Properties 2 打开Java Build Path 发现JDK和Tomcat都未绑定 这是原先项目
  • vue左侧悬浮_vue实现移动端悬浮窗效果

    本文讲述 在使用VUE的移动端实现类似于iPhone的悬浮窗的效果 相关知识点 touchstart当在屏幕上按下手指时触发 touchmove 当在屏幕上移动手指时触发 touchend 当在屏幕上抬起手指时触发 mousedown mo
  • UE4的SpawnActor问题

    1 SpawnActor传参 做项目过程中发现先SpawnActor再传参会先运行 生成的Actor的BeginPlay然后才进行传参 可是我在BeginPlay时就需要用到参数做操作了 发生情况时代码如下 if BaseCharactor
  • 9.21 小米一面面经

    介绍项目经历 测试时用的方式 在公司学到了哪些 有什么积累 将来想发展的方向 手工测试 测试开发 上学时的专业课 手机测试部 通信系统部 和通信关联性比较大 通信协议相关测试 跟手机芯片相关 2G3G4G5G网络 在研产品测试 国际运营商的
  • 计操理论课08 -- openEuler实验第七章文件系统

    文章目录 任务1 为 Ext4 文件系统添加扩展属性 25min 任务描述 任务过程及截图 任务2 注册一个自定义的文件系统类型 15min 任务描述 任务代码 任务截图 任务3 在 proc下创建目录 20min 任务描述 任务代码 任务
  • (LeetCode C++)移动零

    给定一个数组 nums 编写一个函数将所有 0 移动到数组的末尾 同时保持非零元素的相对顺序 请注意 必须在不复制数组的情况下原地对数组进行操作 示例1 输入 nums 0 1 0 3 12 输出 1 3 12 0 0 示例2 输入 num
  • 25道Python练手题(附详细答案),赶紧收藏!python入门

    题目 1 水仙花数 水仙花数 Narcissistic number 也被称为超完全数字不变数 pluperfect digital invariant PPDI 自恋数 自幂数 阿姆斯壮数或阿姆斯特朗数 Armstrong number
  • 启动jar包报错: 找不到或无法加载主类

  • C++ 删除文件夹下所有内容

    介绍一种删除文件夹下所有内容的实现方式 包括删除该文件夹 注 该文件夹下面 无论嵌套多少层文件夹或文件数据 都可以删掉 直接上代码 Tip 以 方式查找所有类型文件 do while方式进行循环遍历 直到满足终止条件 删除文件夹及内容 in
  • web前端开发语言介绍

    web前端开发语言主要包含 html语言 css样式代码 javascript脚本 html5 css3 jQuery ajax Bootstrap Backbone 1 html语言 网页的基本标记语言 也是最基础的语言 掌握起来比较简单
  • 8.cmake常用命令

    前面我们讲到了 cmake 常用的变量 相信 cmake 即编程 的感觉会越来越明显 无论如何 我们仍然可以看到 cmake 比 autotools 要简单很多 接下来我们就要集中的看一看 cmake 所提供的常用指令 在前面的章节我们已经
  • ctfshow-WEB-web11( 利用session绕过登录验证)

    ctf show WEB模块第11关用session中保存的密码进行登录验证 将 session中保存的密码清空即可绕过 页面中直接给了源码 很明显是让我们进行代码审计 源码中将我们输入的密码与 session中保存的密码进行匹配 两个pa
  • stata F值缺失_stata操作问题实录

    一 数据整理 1 新变量带原变量的标签 sysuse auto clear gen m mpg 新建的变量m没有了标签 我想要标签 clonevar Mpg mpg 直接用clonevar命令就好了 2 下载时候就应该直接选合并报表 ege
  • 【Uniapp】base64图片资源转为本地图片,解决canvas不支持base64问题

    通过接口获取到base64类型的二维码 把二维码放到canvas里生成海报 遇到的问题 在微信小程序开发工具中能够正常显示海报 到真机上测试就无法显示二维码 原因 因为canvas不支持base64 其次在使用小程序 canvas 的 dr
  • Logistic回归实战篇之预测病马死亡率(三)

    作 者 崔家华 编 辑 李文臣 四 使用Sklearn构建Logistic回归分类器 开始新一轮的征程 让我们看下Sklearn的Logistic回归分类器 官方英文文档地址 http scikit learn org dev module
  • Python 自动发送邮件详细教程

    自动发送邮件能应用于许多场景中 比如我想要知道股票策略中的股票池是否有实时的更新 这时候如果再拉一遍数据 跑一遍脚本 实在是太浪费时间了 为什么不把这一套流程放到服务器上 然后到点自动运行并发送邮件呢 类似的应用场景还有很多 不仅仅是在股票
  • Cocos2dx中文乱码问题

    最开始在网上找的一个方法 结果在wp8上报错 在windows环境下使用visual studio 开发cocos2d x 由于visual studio 默认编码为GBK 格式 而cocos2d x引擎默认编码为UTF 8 如果有用到中文
  • 从输入网址(URL)到页面加载的全过程

    从输入网址 URL 到页面加载的全过程 简述 输入网址到页面加载的过程涉及知识点众多 所以这里整理一下自己看过的相关文章内容 在浏览器中输入URL 在浏览器中输入URL 也就是网址 URL是Uniform Resource Locator的
  • Queue基本概念

    概念 Queue是一种先进先出的数据结构 他有两个出口 队列容器允许从一段新增元素 从另一端移除元素 队列中只有队头和队尾可以被外界使用 因此队列不允许有遍历行为 队列中进数据称为 入队push 队列中出数据称为 出队pop queue常用
  • Objective-C中的继承与多态, Category, Extension

    1 继承与多态 先要理解实例变量的作用域 再看继承 即 子类的方法和属性 从父类继承得到的方法和属性 子类新增的方法和属性 例子 Person h import