objective-C中的Class(类类型),Selector(选择器SEL),函数指针(IMP)

今天在园子里看到了一篇牛文“Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针”,讲得十分精彩,忍不住把它的代码加上注释整理于此,以便日后查看。

个人体会:obj-C中的“Class类型变量”比c#中的Object基类还要灵活,可以用它生成任何类型的实例(但是它又不是NSObject)。而选择器SEL与函数指针IMP,如果非要跟c#扯上关系的话,这二个结合起来,就点类似c#中的反射+委托,可以根据一个方法名称字符串,直接调用方法。

"牛"的基类 Cattle.h

#import <Foundation/Foundation.h>

@interface Cattle : NSObject {
	int legsCount;
}
- (void)saySomething;
- (void)setLegsCount:(int) count;
@end

 Cattle.m

#import "Cattle.h"

@implementation Cattle

-(void) saySomething
{
	NSLog(@"Hello, I am a cattle, I have %d legs.", legsCount);
}

-(void) setLegsCount:(int) count
{
	legsCount = count;
}
@end

子类“公牛" Bull.h

#import <Foundation/Foundation.h>
#import "Cattle.h"

@interface Bull : Cattle {
	NSString *skinColor;
}
- (void)saySomething;
- (NSString*) getSkinColor;
- (void) setSkinColor:(NSString *) color;
@end

Bull.m

#import "Bull.h"

@implementation Bull

-(void) saySomething
{
	NSLog(@"Hello, I am a %@ bull, I have %d legs.", [self getSkinColor],legsCount);
}

-(NSString*) getSkinColor
{
	return skinColor;
}

- (void) setSkinColor:(NSString *) color
{
	skinColor = color;
}
@end

代理类DoProxy.h (关键的代码都在这里)

#import <Foundation/Foundation.h>

//定义几个字符串常量
#define	SET_SKIN_COLOR @"setSkinColor:"
#define	BULL_CLASS @"Bull"
#define	CATTLE_CLASS @"Cattle"

@interface DoProxy : NSObject {
	BOOL notFirstRun;

	id cattle[3];
	//定义二个选择器
	SEL say;
	SEL skin;

	//定义一个函数指针(传统C语言的处理方式)
	void(*setSkinColor_Func)(id,SEL,NSString*);

	//定义一个IMP方式的函数指针(obj-C中推荐的方式)
	IMP say_Func;

	//定义一个类
	Class bullClass;
}

-(void) doWithCattleId:(id) aCattle colorParam:(NSString*) color;
-(void) setAllIVars;
-(void) SELFuncs;
-(void) functionPointers;

@end

DoProxy.m

#import "DoProxy.h"
#import "Cattle.h"
#import "Bull.h"

@implementation DoProxy

//初始化所有变量
- (void) setAllIVars
{
	cattle[0] = [Cattle new];

	bullClass = NSClassFromString(BULL_CLASS);
	//即cattle[1],cattle[2]都是Bull类的实例
	cattle[1] = [bullClass new];
	cattle[2] = [bullClass new];

	say = @selector(saySomething);
	skin = NSSelectorFromString(SET_SKIN_COLOR);
}

//初始化id
- (void) doWithCattleId:(id) aCattle colorParam:(NSString*) color
{
	//第一次运行的时候
	if(notFirstRun == NO)
	{
		NSString *myName = NSStringFromSelector(_cmd);//取得当前正在执行的方法的名字
		NSLog(@"Running in the method of %@", myName);
		notFirstRun = YES;//修改初次运行标志位
	}

	NSString *cattleParamClassName = [aCattle className];//取得aCattle的"类名称"

	//如果aCattle是Bull或Cattle类的实例
	if([cattleParamClassName isEqualToString:BULL_CLASS] || [cattleParamClassName isEqualToString:CATTLE_CLASS])
	{
		[aCattle setLegsCount:4];//设置牛的4条腿
		if([aCattle respondsToSelector:skin])//如果aCattle对应的是类中,有定义方法"setSkinColor"
		{
			[aCattle performSelector:skin withObject:color];//则调用setSkinColor方法
		}
		else
		{
			NSLog(@"Hi, I am a %@, have not setSkinColor!", cattleParamClassName);//否则输出相应的提示信息
		}
		[aCattle performSelector:say];//最后执行saySomething方法(这二个方法在Bull与Cattle类中都有,所以肯定能运行)
	}
	else //如果aCattle即不是Bull类也不是Cattle类的实例
	{
		NSString *yourClassName = [aCattle className];
		NSLog(@"Hi, you are a %@, but I like cattle or bull!", yourClassName);//显示这个"异类"的相关信息
	}
}

//初始化选择器以及相应函数
- (void) SELFuncs
{
	[self doWithCattleId:cattle[0] colorParam:@"brown"];
	[self doWithCattleId:cattle[1] colorParam:@"red"];
	[self doWithCattleId:cattle[2] colorParam:@"black"];
	[self doWithCattleId:self colorParam:@"haha"];//这里故意传入一个异类self(即DoProxy本身),DoProxy当然不是Bull或Cattle
}

//函数指针测试
- (void) functionPointers
{
	//取得函数指针的第一种方式
	setSkinColor_Func=(void (*)(id, SEL, NSString*)) [cattle[1] methodForSelector:skin];
	//上面的语句其实等效于下面这种方法
	//IMP setSkinColor_Func = [cattle[1] methodForSelector:skin];

	//用第二种方法取得saySomething的函数指针
	say_Func = [cattle[1] methodForSelector:say];

	//用函数指针的形式调用setSkinColor
	setSkinColor_Func(cattle[1],skin,@"verbose");

	NSLog(@"Running as a function pointer will be more efficiency!");

	//调用saySomething方法
	say_Func(cattle[1],say);
}

@end

测试主函数main()

#import <Foundation/Foundation.h>
#import "DoProxy.h"

int main (int argc, const char * argv[]) {
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	DoProxy *doProxy = [DoProxy new];

	[doProxy setAllIVars];
	[doProxy SELFuncs];
	[doProxy functionPointers];

	[doProxy release];
	[pool drain];
	return 0;
}

运行结果:

2011-02-28 21:40:33.240 HelloSelector[630:a0f] Running in the method of doWithCattleId:colorParam:
2011-02-28 21:40:33.245 HelloSelector[630:a0f] Hi, I am a Cattle, have not setSkinColor!
2011-02-28 21:40:33.247 HelloSelector[630:a0f] Hello, I am a cattle, I have 4 legs.
2011-02-28 21:40:33.248 HelloSelector[630:a0f] Hello, I am a red bull, I have 4 legs.
2011-02-28 21:40:33.250 HelloSelector[630:a0f] Hello, I am a black bull, I have 4 legs.
2011-02-28 21:40:33.251 HelloSelector[630:a0f] Hi, you are a DoProxy, but I like cattle or bull!
2011-02-28 21:40:33.252 HelloSelector[630:a0f] Running as a function pointer will be more efficiency!
2011-02-28 21:40:33.254 HelloSelector[630:a0f] Hello, I am a verbose bull, I have 4 legs.

时间: 2024-09-29 12:19:09

objective-C中的Class(类类型),Selector(选择器SEL),函数指针(IMP)的相关文章

详细解读C++编程中的匿名类类型和位域_C 语言

匿名类类型类可以是匿名的 - 也就是说,可以在没有 identifier 的情况下声明类.在将类名称替换为 typedef 名称时,这会很有用,如下所示: typedef struct { unsigned x; unsigned y; } POINT; 注意 上面示例中显示的匿名类的用法对于保留与现有 C 代码的兼容性很有用.在某些 C 代码中,将 typedef 与匿名结构结合使用是很普遍的. 如果您希望对类成员的引用就像它未包含在独立类中的情况一样出现,则匿名类也很有用,如下所示: str

C++中的函数指针与函数对象的总结

以下是对C++中的函数指针与函数对象的使用进行了详细的分析介绍,需要的朋友可以参考下   篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表): 一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(

C++中的函数指针和函数对象总结

篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址.函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数.函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表):一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针的函

深入类的成员函数指针

先看这样一段代码   class test { public:      test(int i){ m_i=i;}      test(){};      void hello()     {          printf("hello/n");     } private:     int m_i; }; int main() {  test *p=new test();  p->hello();  p=NULL;  p->hello(); }   结果是: hello

C语言结构体中的函数指针

引言 指针是C语言的重要组成部分, 于是深入理解指针并且高效地使用指针可以使程序员写出更加老练的程序.我们要记住指针是一个指向内存地址的变量.指针可以引用如int.char--常见的数据类型,例如: int * intptr; // 声明一个指向整型值的指针 int intval = 5 ; // 定义一个整型变量 intptr = & intval ; // intptr现在包含intval的地址 指针不仅仅指向常规的类型还可以指向函数 函数指针 函数指针的内容不难理解,不再赘述,参见<C

详解C语言结构体中的函数指针_C 语言

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的. 函数指针 函数类型 (*指针变量名)(形参列表):第一个括号一定不能少. "函数类型"说明函数的返回类型,由于"()"的优先级高于"*",所以指针变量名外的括号必不可少.  注意指针函数与函数指针表示

java类的问题-java编写从10000到99999种数字中,找到AABCC类型的所有数字

问题描述 java编写从10000到99999种数字中,找到AABCC类型的所有数字 java编写从10000到99999种数字中,找到AABCC类型的所有数字的程序.求指点 解决方案 我不认为本题有必要去"找到"这些类型的数字,我们用程序去生成的效率更高更方便. for(int i=1;i<10;i++){ for(int j=1;j<10;i++){ for(int k=1;k<10;k++){ //如果要求A,B,C不相同,加上 //if(i==j||i==k|

请用C++或C#的类(class)重新实现在list.h和和list.c中定义的AList类型及其操作函数??

问题描述 请从以下三道试题中选择两题完成,并在收到试题的一周内发回结果.其中第二题为必选,并且答题中至少有一道需要用C#完成.我们将以两题的得分之合作为你的测试结果.有完成三题者,则以其中两道得分最高的分数之合作为你的测试结果.你的测试的结果将对我们在是否聘用你以及聘用你的待遇上起非常重要的参考作用.谢谢你的合作和支持.1.请用C++或C#的类(class)重新实现在list.h和和list.c中定义的AList类型及其操作函数.2.CHANGES.txt包含了我公司开发人员对一个代码库的修改历

c++构造函数的初始化列表中初始化了一个类类型的成员,调用的是类类型的复制构造函数吧?

问题描述 c++构造函数的初始化列表中初始化了一个类类型的成员,调用的是类类型的复制构造函数吧? 如题:c++构造函数的初始化列表中初始化了一个类类型的成员,调用的是类类型的复制构造函数吧? 解决方案 什么?复制构造函数? 解决方案二: C++类构造函数列表初始化C++类构造函数初始化列表c++中什么类型的成员变量只能在构造函数的初始化列表中进行 解决方案三: 真是初始化列表,跟复制构造函数没关系,调用复制构造函数要看你是如何初始化的 解决方案四: 参数列表初始化成员是调用成员的构造函数,但是什