OC-方法的本质

本文详细介绍了Objective-C中的方法表示方法,包括SEL(消息选择器)、IMP(函数指针)和Method结构体。讲解了如何通过SEL找到IMP执行相应方法,以及如何使用performSelector发送消息。同时,解释了methodForSelector和respondsToSelector等方法的作用,展示了打印类方法列表的示例代码。
摘要由CSDN通过智能技术生成

method_t结构体

struct method_t {
    SEL name;//存储消息
    const char *types;//返回值种类
    IMP imp;//指向name的方法在内存中的地址

/*下面这部分看不懂*/
    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

SEL(消息选择器)

是一种数据类型 也是typedef类型 已经加了*
SEL是一个类型
SEL对象是一个唯一标识用来存储一个方法名 不包含参数和返回值类型(消息选择器+参数类型+返回值类型 = 签名(signature))

获取SEL @selector

SEL s1 = @selector(fun);//这里就是获取到了fun的SEL消息

这样s1完全等价于这个方法名

_cmd (当前这个方法的selector)

每个类对象都有的 跟self的性质类似

merhod signature(方法签名)

是什么?

SEL,Method,IMP

SEL很熟悉了 就是消息的名字 所以你单纯拿到一个SEL是根本不知道要做什么

IMP是 typedef id (*IMP)(id, SEL, ...) 函数指针 函数指针就存储某个函数的地址
id是这个函数的返回值 id和SEL是需要的参数
IMP就是指向一个已经定义了的方法在内存中的地址 也就是你单纯拿到IMP你是能知道要干嘛的
IMP有两个参数 id 是一个是指向self的指针 SEL就是当前指针指向方法的名字

Method包含了SEL和IMP 这个应该就是传统意义上理解的方法
我觉得应该通过self找到分发表 然后在分发表中通过SEL找到对应的IMP 通过IMP就能知道定义的函数地址在哪

可以这么理解 每个class都有一个分发表 表中的每一个实体都代表一个方法(Method) 一个Method的名字叫做SEL然后地址被IMP存储了

还有个method_type 是char指针 存储方法的参数类型和返回值类型

void objc_msgSend(id self, SEL cmd,…);//是一个可变参数函数

编译器会把一个消息表达式转换为objc_msgSend方法的调用 如
[receiver(接受消息的对象) message(消息):参数] 转化为 objc_msgSend(receiver(接受消息的对象),selector(@selector(message)),参数)
例如

[a func:x];//被转化为
objc_msgSend(a,@selector(func),x);

手动发送SEL消息

SEL s1 = @selector(fun);//这里就是获取到了fun的SEL消息
[a performSelector:s1];//发送s1的SEL消息给a对象 类比与[a fun];
//这样a对象就会通过ISO指针找到他在代码段中的类 去他的类里面找是否
//有与s1消息相匹配的SEL消息

//如果此消息是带有参数的
SEL s2 = @delector(fun1:);//这里就必须带:冒号 因为函数名是fun1:(NSString *)str{}
[a performSelector:s2 withObject:@"hello"];
//多个参数就是[a performSelector:s2 withObject:@"hello" withObject:1];
//最多只能传两个参数 因为这个方法只重装了一个参数 和两个参数版本
//如果需要更多参数 只能将参数都放在一个类里面 然后将这个类作为参数

和C++函数指针做对比

你可能会觉得SEL就是一个函数指针 但是只能说很像
但是函数指针是通过指针记录函数在内存中的地址,只能静态的调用,也就是说你记的是哪个函数的地址就只能调用哪个函数,在编译的时候就决定了,而SEL只是记录了函数名,但是不同的类,不同的对象可能相应的函数并不一样,所以SEL会在运行时才能确定要调用的是哪个函数,动态的执行不同的方法

OC也可以使用函数指针(因为以函数的方式调用方法还是更快些)

@interface A:NSObject
-(void)funcWithAge:(int)a num:(int)n;
@end;
IMP funcp; // IMP是implementation
funcp = [a methodForSelector@select(A:func)];//说实话没弄懂 methodForSelector的参数是SEL 那怎么能知道具体函数的地址,不还是动态调用吗 和SEL调用有什么区别 是只省略了一个去找这个函数的过程吗
xyz = (*funcp)(a,@select(A:func),x,y);// x,y是参数

(IMP)methodForSelector:(SEL)aSelector 搜索和指定选择器相对应的方法,并返回指向该方法实现的函数指针

NSStringFromSelector从选择器获取方法名

NSString *funcName;
funcName = NSStringFromSelector(selector);

respondsToSelector(判断该类中是否有该对象方法)

不能判断是否有类方法

A *a = [A new];
BOOL b1 = [a resondsToSelector:@selector(funa)];
if(b1 == YES)
{
	[a funa];
}

重写respondsToSelector

-(BOOL)respondsToSelector:(SEL)aSelector
{
	if([super respondsToSelector:aSelector])//递归调用 查找父类中是否有这个函数
		return YES;
	if([self methodForSelector:aSelector] != (IMP)NULL)//递归到顶层 也就是NSObject类 就会执行这一条 这个直接返回对应方法的函数指针
		return YES;
	if([content respondsToSelector:aSelector])
		return YES;
	return NO;
}

merhodForSelector:(直接返回对应方法的函数指针)

可以这样写

[self methodForSelector:aSelector] != (IMP)NULL

判断是否找到

instancesRespondToSelector(判断该类中是否有该类方法)

BOOL b1 = [A instancesRespondToSelector:@selector(funa)];

isKindOfClass(判断对象类型是否是该类或子类)

A *a = [A new];
A *a1 = [B new];
A *b = [B new];
B *b1 = [A new];
BOOL s1 = [a isKindOfClass:[A class]];//判断a对象的类型是否是A类型或者A的子类型 结果是1
BOOL s2 = [b isKindOfClass:[A class]];//1 
BOOL s3 = [a1 isKindOfClass:[A class]];//1
BOOL s4 = [b1 isKindOfClass:[A class]];//1

isMemerOfClass(判断对象类型是否是该类 注意不包括子类)

A *a = [A new];
A *a1 = [B new];
A *b = [B new];
B *b1 = [A new];
BOOL s1 = [a isMemerOfClass:[A class]];//判断a对象的类型是否是A类型或者A的子类型 结果是1
BOOL s2 = [b isMemerOfClass:[A class]];//0
BOOL s3 = [a1 isMemerOfClass:[A class]];//0
BOOL s4 = [b1 isMemerOfClass:[A class]];//1

很明显是否为该类型主要看创建对象的时候是什么类型 而不是看指针类型

isSubclassOfClass(判断一个类是否为另一个类的子类)

BOOL s1 = [B isSubclassOfClass:[A class]];//判断B是否为A的子类

手写一个打印方法列表

-(void)printClassAllMethod(Class)CLSIZE{
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(cls,&count);
    for(int i =0; i<count;i++)
    {
        Method method = methodList[i];
        SEL sel = methofImplementation(cls,sel);
        NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
    }
    free(methodList);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值