介绍@dynamic的用法

介绍@dynamic的用法

 

    Objective-C 2.0提供了属性(@property),可以让编译器自动生成setter和getter方法。如果不想编译器自作主张生成这些setter和getter方法,则使用@dynamic。举个简单例子,如下

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (copy) NSString *name;
@end

@implementation Person
// @dynamic tells compiler don't generate setter and getter automatically
@dynamic name;
@end

int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    Person *a = [[Person alloc] init];
    
    a.name = @"Hello"; // will crash here
    NSLog(@"%@", a.name);
    
    [a release];
    [pool drain];
    
    return 0;
} // main

    运行该程序,Xcode会报错“-[PersonsetName:]: unrecognized selector sent to instance 0x1001149d0”。如果将@dynamic注释掉,则一切Ok。

    这里由于使用@dynamic,我们需要自己提供setter和getter方法。一般有两种方法:1)自己提供setter和getter方法,将编译器自动生成的setter和getter方法手动再写一遍;2)动态方法决议(DynamicMethod Resolution),在运行时提供setter和getter对应实现的C函数。

    对于第一种方法,需要在类中显式提供实例变量,因为@dynamic不能像@synthesize那样向实现文件(.m)提供实例变量。

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    // must provide a ivar for our setter and getter
    NSString *_name;
}
@property (copy) NSString *name;
@end

@implementation Person
// @dynamic tells compiler don't generate setter and getter automatically
@dynamic name;

// We provide setter and getter here
- (void) setName:(NSString *)name
{
    if (_name != name) {
        [_name release];
        _name = [name copy];
    }
}

- (NSString *) name
{
    return _name;
}
@end // Person

int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    Person *a = [[Person alloc] init];
    
    a.name = @"Hello"; // Ok, use our setter
    a.name = @"Hello, world";
    NSLog(@"%@", a.name); // Ok, use our getter
    
    [a release];
    [pool drain];
    
    return 0;
} // main

    对于第二种方法,在运行时决定setter和getter对应实现的C函数,使用了NSObject提供的resolveInstanceMethod:方法。在C函数中不能直接使用实例变量,需要将ObjC对象self转成C中的结构体,因此在Person类同样需要显式声明实例变量而且访问级别是@public,为了隐藏该实例变量,将声明放在扩展(extension)中

#import <Foundation/Foundation.h>
#import <objc/objc-runtime.h> // for class_addMethod()

// ------------------------------------------------------
// A .h file
@interface Person : NSObject
@property (copy) NSString *name;
- (void) hello;
@end

// ------------------------------------------------------
// A .m file
// Use extension to override the access level of _name ivar
@interface Person ()
{
@public
    NSString *_name;
}
@end

@implementation Person
// @dynamic implies compiler to look for setName: and name method in runtime
@dynamic name;

// Only resolve unrecognized methods, and only load methods dynamically once
+ (BOOL) resolveInstanceMethod:(SEL)sel
{
    // Capture setName: and name method
    if (sel == @selector(setName:)) {
        class_addMethod([self class], sel, (IMP)setName, "v@:@");
        return YES;
    }
    else if (sel == @selector(name)) {
        class_addMethod([self class], sel, (IMP)getName, "@@:");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

void setName(id self, SEL _cmd, NSString* name)
{
    // Implement @property (copy)
    if (((Person *)self)->_name != name) {
        [((Person *)self)->_name release];
        ((Person *)self)->_name = [name copy];
    }
}

NSString* getName(id self, SEL _cmd)
{
    return ((Person *)self)->_name;
}

- (void) hello
{
    NSLog(@"Hello, world");
}

@end // Person

int main(int argc, const char * argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    Person *a = [[Person alloc] init];
    [a hello]; // never call resolveInstanceMethod
    
    a.name = @"hello1";
    NSLog(@"%@", a.name);
    a.name = @"hello2";
    NSLog(@"%@", a.name);
    
    [a release];
    [pool drain];
    
    return 0;
} // main


    总结以上,@dynamic的作用就是禁止编译器为@property产生setter和getter方法,有两种办法实现setter和getter方法:1)自己提供setter和getter方法;2)方法动态决议(DynamicMethod Resolution)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值