获取Class
在OC中每一个类都有一个对应的Class,Object-C程序中获得Class通常有如下3中方式:
- 使用Class NSClassFromString(NSString* aClassName)函数来获取Class,该函数需要传入字符串参数,该字符串参数的值为某个类的类名。
- 调用某个类的class方法来获得对应的Class。
- 调用某个对象的class方法,由于该方法是NSObject类中的一个方法,所以所有的Object-C对象都可以调用该方法,该方法返回该对象所属类对应的Class。
Class class=NSClassFromString(@"NSDate");
NSLog(@"%@",class);
id date=[[class alloc]init];
NSLog(@"%@",date);
NSLog(@"%@",[date class]);
NSLog(@"%d",class==NSDate.class);
以上测试了3种方法来创建class,得到的结果为:
---------------------------------------------------------------------------------------------------------------------------------
2017-04-11 17:45:16.340 Prj4[1118:1266051] NSDate
2017-04-11 17:45:16.346 Prj4[1118:1266051] 2017-04-11 09:45:16 +0000
2017-04-11 17:45:16.346 Prj4[1118:1266051] __NSDate
2017-04-11 17:45:16.347 Prj4[1118:1266051] 1
Program ended with exit code: 0
---------------------------------------------------------------------------------------------------------------------------------可以看到,利用[data class]得到的是_NSDate而不是NSDate,这个是由于程序直接调用date对象的class方法来获取Class时返回NSDate的子类。NSDate只是这个类簇的前端,当程序调用[[NSDate alloc] init]创建对象时,程序返回的只是NSDate的子类实例。
检查继承关系
如果程序只需要确认一个类的继承关系,比如判断它是否是某个类的实例或是否为某个类及其子类的实例,可以利用NSObject提供的如下方法:
- isKindOfClass:该方法需要传入一个Class参数,用于判断该对象是否为该类及其子类的实例;
- isMemberOfClass:该方法需要传入一个Class参数,用于判断该对象是否为该类的实例;
- conformsToProtocol:该方法需要传入一个Protocol参数,用于判断该对象是否为该类及其子类的实例。
- 利用@protocol指令来实现 //@protocol(协议名);
- 调用Protocol* NSProtocolFromString:(NSString*) nameStr //根据协议名来获取到对应的协议。
当得到对应的类以后,便可以利用KVC机制来进行对方法或者变量的调用。此时,无论变量或者函数出于什么访问权限,或者出于什么位置,均可以利用KVC机制来进行设置、访问实例变量的值。
如果程序需要判断某个对象是否可调用方法,可通过NSObject的respondsToSelector方法来进行判断。该方法传入一个sel参数,sel即为一个函数。为了动态获取到SEL对象,OC提供了@selector指令来获取当前类中指定的方法,需要方法的完整签名。根据字符串获取SEL: NSSelectorFromString(NSString* aSeletcorName).
注意:@selector获取的是当前类中所指定的方法,@selector()基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取.
@selector返回一个SEL对象。
[object @selector(方法名:方法参数..) ] 返回object对应类相应的方法。
SEL class_func ; //定义一个类方法指针
class_func = @selector(add:int);
执行selector值
取得相应值后,怎么处理SEL值呢,这一点仍然与函数指针一样,就是执行它
函数指针执行,(以下有几种等效形式)
*c_func(10);
c_func(10);
SEL变量的执行要用performSelecor方法来执行.
[对象 performSelector:SEL变量 withObject:参数1 withObject:参数2];
selector的应用场合
selector本质是跟C的回调函数一样。主要用于两个对象之间进行松耦合的通讯.这种方法基本上整个Cocoa库之间对象,控制之间通讯都是在这个基础构建的。
如果程序需要动态调用对象的普通方法,可以通过下面的方法:
- 通过利用NSObject提供的performSelector方法来实现,如果需要传入参数,还可以通withObject标签来传入参数。
- 使用objc_msgSend(receiver,selector,...)函数来调用。第一个参数是调用者,第二个参数代表调用的方法,接下来的参数代表要传入的参数。需要导入#import <obj/message.h>头文件。
案例代码如下:
#import <Foundation/Foundation.h>
@interface LWBCar2 : NSObject
@end
LWBCar2.m文件如下:
#import "LWBCar2.h"
#import<objc/message.h>
#import <objc/runtime.h>
@implementation LWBCar2
-(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]];
// (void *)objc_msgSend(self, @selector(move:),[NSNumber numberWithInt:3]);
void (*objc_msgSendTyped)(id self, SEL _cmd, id obj) = (void *)objc_msgSend;
objc_msgSendTyped(self,@selector(move:),[NSNumber numberWithInt:3]);
return 100*factor;
//objc_msg
}
@end
由于版本的提升,对于objc_msgSend必须用一个函数指针来取。
测试代码
Class clazz=NSClassFromString(@"LWBCar2");
id car=[[clazz alloc]init];
[car performSelector:@selector(addSpeed:) withObject: [NSNumber numberWithDouble:3.4]];
利用反射技术来生成对象,并动态调用方法。结果:
2017-04-11 20:13:15.504 Prj4[1279:1626027] 汽车正在路上走~~0
2017-04-11 20:13:15.506 Prj4[1279:1626027] 汽车正在路上走~~1
2017-04-11 20:13:15.506 Prj4[1279:1626027] 汽车正在路上走~~0
2017-04-11 20:13:15.506 Prj4[1279:1626027] 汽车正在路上走~~1
2017-04-11 20:13:15.506 Prj4[1279:1626027] 汽车正在路上走~~0
2017-04-11 20:13:15.506 Prj4[1279:1626027] 汽车正在路上走~~1
2017-04-11 20:13:15.507 Prj4[1279:1626027] 汽车正在路上走~~2
Program ended with exit code: 0