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