Objective-C面向对象(四)
1.协议(protocol)和委托
1.1 规范、协议与接口
OC中协议的作用就相当于其他语言中接口的作用。协议定义的是多个类共同的公共行为规范,协议通常定义一组公用方法,但不提供实现。
1.2 定义
正式协议使用@protocol关键字来定义,语法如下:
@protocol 协议名<父协议1,父协议2>
{
//方法定义
}
一个协议可以有多个直接父协议,但协议只能继承协议不能继承类。协议中定义的方法只有方法签名,没有方法实现;协议中定义的方法既可以是类方法,也可以是实例方法。
1.3 实现协议
在类定义的接口部分可指定该类继承的父类和实现的协议
@interface 类名:父类 <协议1,协议2...>
@optional
@required
1.4 协议和委托
2.使用@try处理异常
2.1 使用@try...@catch...@finally捕捉异常
OC提供了@try@catch@finally来捕捉异常。
@try
{
//业务实现代码
}
@catch(异常1 ex)
{
//异常处理
}
@catch(异常2 ex)
{
//异常处理
}
..
@finally
{
}
所有异常对象都包含下面几个常用方法:
- name 返回该异常详细的名称
- reason 返回该异常的原因
- userInfo 返回引发该异常的用户附加信息,返回一个NSDictionary对象
在程序中自行抛出异常,使用@throw语句 @throw ExceptionInstance;
3.反射
OC提供了3中方式与运行环境交互
- 直接通过OC的源代码
- 通过NSObject类中定义的方法进行动态变成
- 直接调用运行时函数进行动态编程
3.1 获得Class
每个类都有一个对应的Class,OC获得Class的三种方式:
- 使用Class NSClassFromString(NSString* aClassName)函数来获取Class,字符串参数是某个类的雷鸣
- 调用某个类的class方法来获取该类对应的Class。
[FKItem class]
- 调用对象的class方法
3.2 检查继承关系
- -isKindOfClass ,传入一个Class参数,判断该对象是否该该类及其子类的实例
- -isMemberOfClass ,传入CLass参数,判断该对象是否为该类及其子类的实例
- -conformsToProtocol ,传入一个Protocol参数,判断该对象是否为该类及其子类的实例
获取Protocol参数的方法
- OC提供@protocol指令
- 调用 Protocol* NSProtocolFromString(NSStirng* namestr)
3.3 动态调用方法
判断某个对象是否可调用方法,可通过NSObject的如下方法进行判断:
respondsToSelector:
传入一个SEL
参数-OC 把方法成为选择器,
OC中动态获取SEL对象的方法
- 使用@selector指令来获取当前类中制定的方法。该指令需要用完整的方法签名关键字作为参数
- 使用SEL NSSelectorFromString(NSString* aSelectorName)函数根据方法签名关键字的字符串来获取对应方法。
动态调用对象的普通方法,两种方式实现:
- 用过NSObject的performSelector:方法实现,第一个参数传入SEL对象,如果调用方法需要参数,可以通过withObject:标签传入参数。
- 使用 objc_msgSend(receiver,selector,...),第一个参数是方法调用者,第二个参数是调用的方法,接下来的参数作为调用方法的参数。要导入 <objc/message.h>头文件
-(IMP)methodForSelector:(SEL)aSelector
: 传入SEL参数,返回对应的IMP对象。
IMP相当于一个指向函数的指针变量,也就是代表了函数的入口,可以通过IMP来调用该函数.
返回值类型 (* 指针变量名)(id,SEL,...);
#import <Foundation/Foundation.h>
#import <objc/message.h>
@interface FKCar:NSObject
@end
@implementation FKCar
-(void) move:(NSNumber*) count
{
int num =[count intValue];
for (int i = 0; i < num; ++i)
{
NSLog(@"%@",[NSString stringWithFormat:@"汽车正在路上走~~%d",i]);
}
}
-(double) addSpeed:(double)factor
{
[self performSelector:@selector(move:)
withObject:[NSNumber numberWithInt:2]];
[self performSelector:NSSelectorFromString(@"move:")
withObject:[NSNumber numberWithInt:2]];
objc_msgSend(self,@selector(move:)
,[NSNumber numberWithInt:3]);
objc_msgSend(self,NSSelectorFromString(@"move:"),[NSNumber numberWithInt:3]);
NSLog(@"正在加速。。。%g",factor);
return 100 * factor;
}
@end
int main(int argc, char const *argv[])
{
@autoreleasepool{
//获取FKCar类
Class clazz = NSClassFromString(@"FKCar");
//动态加载FKCar对象
id car = [[clazz alloc] init];
//使用performSelector:方法来动态调用方法
[car performSelector:@selector(addSpeed:)
withObject:[NSNumber numberWithDouble:3.4]];
//使用objc_msgSend() 函数动态调用方法
objc_msgSend(car,@selector(addSpeed:),3.4);
//定义函数指针变量
double (*addSpeed)(id,SEL,double);
//获取car对象的addSpeed:方法,并将该方法赋值给addSpeed函数指针变量
addSpeed = (double(*)(id,SEL,double))[car
methodForSelector:NSSelectorFromString(@"addSpeed:")];
//通过addSpeed函数指针变量来调用car对象的方法
double speed = addSpeed(car ,@selector(addSpeed:),2.4);
//输出调用方法的返回值
NSLog(@"加速后的速度为:%g",speed);
}
}
4.手动内存管理
OC可用的内存回收机制有三种:
- 手动引用计数和自动释放池
- 自动引用计数(ARC Automatic Reference Counting)
- 自动垃圾回收(iOS 不支持)
4.1 对象的引用计数
OC采用了一种引用计数(Reference Counting)的计数来跟踪对象的状态。
在手动计数中,改变对象的引用计数的方式如下:
- 当程序调用方法名以alloc、new、copy、mutableCopy开头的方法来创建对象时,该对象的引用计数加1
- 程序调用对象的retain方法时,该对象的引用计数加1
- 程序调用对象的release方法时,该对象的引用计数减1
NSObject提供了有关引用计数的如下方法:
- -retian: 将对象的引用计数器加1
- -release: 将该对象的引用计数器减1
- -autorelease: 不改变对象的引用计数器的值,只是将对象添加到自动释放池
- -retainCount: 返回该对象的引用计数的值
4.2 对象所属权
手动释放内存的基本思路应该遵循:谁(对象、函数)把对象的引用计数加1,就要在销毁前把该对象的引用计数减1.