介绍@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 charchar * 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 charchar * 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 charchar * 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)。