剖析Object-C中的属性与消息传递机制

来源:互联网 发布:mac os x 10.7.4 编辑:程序博客网 时间:2024/05/17 07:03


一、属性

属性是OC的一项特性,用于封装对象中的数据,OC对象通常会吧其所需要的数据保存为各种实例变量,实例变量一般都是通过存取方法来访问的。

例如有一个Person类,该类具有姓名和年龄等属性

#import <Foundation/Foundation.h>@interface Person : NSObject@property (nonatomic,assign) int age;@property (nonatomic,assign) NSString *name@end
OC中,实例变量(例如Person中的age)会被当作一种存储偏移量所用的特殊变量,所谓的偏移量就是硬编码,它表该变量距离存放对象的内存区域的起始地址还有多远,OC会把该变量交由类对象(Person就是一个类对象)保管偏移量会在运行期查找,这样做的好处就是如果类的定义变了,那么它存储的偏移量也会跟着改变,这样的话,无何时访问实例变量,总能使用正确的偏移量,甚至是可以在运行期向类中新增实例变量,这就是稳固的应用程序二制接口ABI,OC的这一特性主要是体现在分类或者是实现文件中定义实例变量。


二、消息传递

Object-C中,在对象之间传递数据并执行任务的过程就叫做消息传递,例如在Person类中有个setAge的方法,当我们创建一个对象并调用了setAge这个方法时,这个过程就是消息传递,setAge:是这个消息的名称,而10是这个消息的参数,两者结合起来称为消息

Person *p = [[Person alloc] init];[p setAge:10];
我们知道, 在Object-c里消息是运行期绑定的,也就是说只有在运行时才知道有这个消息和它在内存中所在的位

置。如果向某个对象传递消息,那就会使用动态绑定机制来决定需要调用的方法,在底层,所有方法都是普通的C育秧函数,然而对象收到消息之后,究竟调用哪个方法完全取决于运行期,甚至是可以在程序运行时改变。

给对象发送消息可以这样来写:

id returnValue = [someObject messageName:parameter];

其中someObject是对象,也就是接收者,messageName叫selector,可以称之为“选择子”,编译器看到此消息后,将其转换为一条标准的C语言函数调用,所调用的函数乃是消息传递机制中的核心函数,叫做objc_msgSend,它的原型

如下:

voidobjc_msgSend(id self,SEL cmd,...)

这是个参数个数可变的函数,能接受两个或两个以上的参数,第一个参数代表接收者,第二个参数是SEL类型的选择
子,后续参数是消息中的参数。选择子指的就是方法的名字,编译器会把刚才哪个例子转换为如下函数
id returnValue = [someObject @selector(messageName:),parameter];
objc_msgSend函数会依据接收者与选择子的类型来调用适当的方法,为了完成此操作,该方法需要在接收者所属的类中搜寻其方法列表,如果能找到与选择子名称相符的方法,则跳至该其实现代码,若是找不到,那就沿着继承体系继续往上查找,等找到合适方法之后再跳转,如果最终还是找不到相符的方法,就会执行消息转发操作,转给系统的其他类来处理,如果再找不到相符合的方法,那就以程序崩溃告终。
消息传递机制虽然会经过许多步骤,但是objc_msgSend会将匹配结果缓存在快速映射表里面,每个类都有这样一块缓存,若是稍后还向该类发送与选择子相同的消息,那么执行起来就可以很快了。
利用这种特性,我们也可以来实现与java中反射一样的效果
代码示例:
#import <Foundation/Foundation.h>@interface Person : NSObject@property (nonatomic,assign) int age;- (Person *)initWithAge:(int)age;@end#import "Person.h"@implementation Person- (Person *)initWithAge:(int)age{    if (self = [super init]) {        _age = age;    }    return self;    }@end
#import "Person.h"@interface Student : Person@end#import "Student.h"@implementation Student@end
#import <Foundation/Foundation.h>#import "Person.h"#import "Student.h"int main(){        // 类的反射    Class name = NSClassFromString(@"Person");    Person *p = [[name alloc] init];    NSLog(@"%@",p);        // 判断对象是不是属于指定类型或其子类        Student *stu = [[Student alloc] init];        if ([stu isKindOfClass:[Person class]])    {        NSLog(@"%@是%@类型或子类",stu,[Person class]);    }    else    {        NSLog(@"%@不是%@类型或子类",stu,[Person class]);    }        // 判断对象是不是属于指定类型        if ([stu isMemberOfClass:[Person class]]) {        NSLog(@"stu是Person类型");    }    else    {        NSLog(@"stu不是Person类型");    }        // 判断对象有没有某个方法        SEL sel = NSSelectorFromString(@"setAge");    if ([stu respondsToSelector:sel]) {        NSLog(@"有该方法");    }    else    {        NSLog(@"没有该方法");    }        // 动态调用对象方法    Person *p1 = [[Person alloc] initWithAge:1];    NSNumber *age = [p1 performSelector:@selector(age)];    int age1 = [age intValue];        // Incompatible pointer to integer conversion initializing 'int' with an expression of type 'id'    // 用‘id’类型的表达方法的不兼容的指针来转换初始化‘int’        NSLog(@"%d",age1);            // 调用有参数的    Person *p2 = [[Person alloc] init];    [p2 performSelector:@selector(setAge:) withObject:@"2"];    NSLog(@"%d",p2.age);        // 判断对象是否遵守某个协议    BOOL conforms = [p2 conformsToProtocol:@protocol(NSObject)];    NSLog(@"%d",conforms);           return 0;}

参考资料:《Effective Object-C 2.0》

0 0