多态
多态的概念和产生
变态:相同类型的对象,在同一件行为(方法),但某个对象表现成与众不同的行为(态),此时就称之为变态。
多态:相同类型的对象,在执行同一个方法时,表现成多种的行为特征(态),此时就称之为多态。
多态为何会产生?
1. 支持向上转型。子类实例可以赋值给父类变量。
2. 方法调用时总是动态绑定的。
如果有这两点情况,程序就会产生多态。
多态的好处:编程更加灵活。
—— 当函数、方法需要一个父类参数时,实际可以传入给父类的任意子类的实例。类型判断和类型转换
id类型,有点类似NSObject类型。但id类型更强大,它在编译时就支持动态解析【方法】。成员变量不行!点语法不行!
在有些时候,希望判断某个变量所指的对象是否为指定类型的实例。
- [变量 isKindOfClass:类] : 判断该变量所指对象是否为指定类、及其子类的实例。
- [变量 isMemberOfClass:类] : 判断该变量所指对象是否为指定类的实例。
- + isSubcassOfClass:clazz:判断该类是否为指定类的子类。
向上转型:Object-C支持自动装换,无需程序进行额外处理。
向下转型(缩小转换):就必须使用强制类型转换的语法。 (类型)
包装类
为什么要包装类:有些方法需要的参数必须是id类型(NSObject类型),但实际传入的值又是基本类型。
此时就需要借助于包装类把基本类型的值包装为对象。
包装
基本类型 --> 对象
拆包
对象 ---> 基本类型的值
典型地,KVC时就需要使用包装类。
这些都不不是包装类。
NSInteger,(32位平台上,相当于int或,64位平台上,相当于long)
NSUInteger, (32位平台上,相当于unsignedint或,64位平台上,相当于unsigned long)
CGFloat(32位平台上,相当于float、64位平台,相当于double)
NSValue和NSNumber, NSNubmer是NSValue的子类。
NSValue是一个通用的包装类,可以包装基本类型、指针、对象id等。实际上用的少。
NSNumber:用于包装数值类型(short、int、long、long long、float、double、char、BOOL、枚举)。
+ numberWithXxx: 将基本类型的值,包装为NSNumber对象。
- initWithXxx: 将基本类型的值,包装为NSNumber对象。
- xxxValue:将包装类对象拆箱出所包装的值。
处理对象
- description方法
该方法来自NSObject,因此所有类的实例都可调用该方法。
当程序输出一个对象时,实际上就是输出该对象的description方法的返回值。
NSObject的description方法的默认返回 <类名: 地址>
如果程序希望输出对象时,能看到该对象的内部状态(各成员变量的值),那么最好就重写description方法。
- isEqual:
如果用==判断两个指针变量是否相等,程序要求两个指针变量指向内存中同一个对象才算相等。即要求两个指针变量保存的地址相等。
在某些时候,程序希望根据业务要求来判断两个对象是否相等 —— 比如对于两个苹果,只要他们品种、重量相等,即可认为它们相等。
再比如在某些社交APP中,只要两个用户的id相等,即可认为它们相等。此时就考虑重写isEqual:方法。
类别(category,分类)
假如项目需要为一个已有类(比如NSNumber)增加新的方法,有如下两种做法:
- 通过继承创建NSNumber的子类,然后在子类中增加新的方法。
这种方式的结果是:NSNumber的程序员扩展的子类就具有了新方法,但NSNumber类本身依然没有新的方法。
NSNumber类簇中其他类更加没有这些新的方法。
更糟糕的是:Object-C的很多类采用的是一种类簇(cluster)设计,NSNumber、NSDate等都是这种设计。
类簇设计:Cocoa框架提供一个公开的类,但在该类底层隐藏大量的子类,这个公开的类就被称为类簇的前端类。
当程序面向公开API(比如NSNumber、NSDate)等编程,但实际上系统创建的只是该类的子类的实例。
- 利用类别:类别可以在不修改已有类的代码的基础上,为已有的类增加新的方法。
Object-C中并不太喜欢用继承。
类别的语法:
@interface 已有的类 (类别名)
方法声明
@end
【注意】:类别只能添加方法,不能添加成员变量,因此也不能使用@property来合成属性。
@implementation 已有的类 (类别名)
方法实现
@end
类别:体现Object-C语言的动态性,通过类别,程序可以在不修改代码的基础上,为已有的类,动态地增加新的方法。
类别可用于对类进行模块化设计。
在Cocoa框架中,有很多类非常庞大,比如NSWindow类,它包含100多个方法,这个类的代码就非常多,程序难以维护。
此时就可以考虑如下设计:
(1)建一个NSWindow类,该类中只定义最核心的20个方法,这个类代码就少的多了。
(2)为了NSWindows创建多个类别,每个类别再额外定义10来个方法,每个类别的代码也不会太多。
此处通过类别定义的方法最后也会添加NSWindow中。
通常来说,集中某些功能会放置到特定的类别中。
NSString也采用了类别进行模块化设计。该String类中用于操作文件路径的相关方法,都放在特定分类(模块)中实现。
类别还可以用于暴露私有方法。
Object-C的方法之所以能被隐藏,是因为在类的头文件部分没有声明该方法。
为了暴露该方法,只要使用类别的头文件重新声明该方法即可。
而且在使用时,也很灵活:
1. 如果你希望还是隐藏该方法,只要导入该类原始的头文件即可。
2. 如果你希望把隐藏方法暴露出来,你就应导入类别的头文件即可。
还可用于定义非正式协议。
扩展(extension)
扩展,相当于一个匿名的类别。
但扩展即可添加新的方法,也可添加新的成员变量。当然也可以用property合成属性。
@interface 已有的类 ()
{
// 成员变量
}
方法声明
@end
扩展定义,通常直接放在实现部分即可。
而且扩展添加的方法,通常直接在类的实现部分实现它们即可。
如果将扩展定义放在类的实现部分,那么该扩展中定义的方法、成员变量都是被隐藏的。
如果希望将扩展定义的东西也暴露出来,那么就需要使用额外的头文件来定义扩展,并使用使用时导入扩展的头文件。
协议
协议,体现的一种高层次的规范,这种通常用于制定某种规定,用于被类来遵守。
Object-C,某个类,遵守(实现)了xxx协议。
【协议,自身不提供实现。】
Java中的接口,与OC的协议完全一样的。
还可用于定义非正式协议(unformalprotocol)。 ——用到的相对较少
非正式协议,就是为NSObject定义一个类别的头文件部分即可——因为协议是不提供实现的!!
实现非正式协议,其实是通过继承NSObject的指定分类来实现的。
非正式协议,编译不会检查实现类必须实现协议中的方法。
如果实现类要调用非正式协议中的方法,必须自己保证在实现类中实现该方法。
正式协议
@protocol 协议名 <父协议1, 父协议2, ...>
// 方法声明
@end
协议没有实现部分!
1. Object-C类是单继承的,但协议是多继承的。协议,可以没有父协议的。
2. 协议只能继承继承,协议不能继承类。
遵守协议
@interface 类名 : 父类 <协议1, 协议2, 协议3...>
@end
遵守协议,就是在父类后面用尖括号列出所有需要遵守的协议。
如果一个类遵守了正式协议,编译器会要求实现类必须实现协议中所有方法,编译器会报警告。
正式协议中可选方法 - 遵守协议的类,可实现该方法,也可不实现该方法。
此时应该使用@optional来限定该方法。
@required要求该方法必须被实现 —— 默认就是它,因此一般不写。
协议的典型用途: 协议与代理。
无论在Cocoa开发,开始CocoaTouch开发中,这都是非常常见。
在我们Cocoa、Cocoa Touch开发中,经常会需要设置代理对象,而代理对象总是要实现特定的协议,并实现协议中特定的方法。