@synthesize vs @dynamic,有什么区别?


Answers:


744

@synthesize将为您的属性生成getter和setter方法。@dynamic只是告诉编译器,getter和setter方法不是由类本身实现的,而是由其他地方实现的(例如超类或将在运行时提供)。

@dynamic的使用例如是NSManagedObject(CoreData)的子类,或者当您要为由超类定义的属性(未定义为插座)创建插座时。

@dynamic也可用于委派实现访问器的责任。如果您在类中自己实现访问器,则通常不使用@dynamic。

超级班:

@property (nonatomic, retain) NSButton *someButton;
...
@synthesize someButton;

子类:

@property (nonatomic, retain) IBOutlet NSButton *someButton;
...
@dynamic someButton;

25
不是100%正确;如果未设置@synthesize或@dynamic,则dynamic为默认设置。指定@dynamic仅意味着您有责任根据属性声明的签名正确地实现属性访问器。
凯夫拉尔

68
并非如此,@dynamic被委派了实现访问器的责任。如果您在类中自己实现访问器,则通常不使用@dynamic。
deaderikh

2
我得到NSUnknownKeyException的错误与我的动态特性,当我删除了@synthesize行(Xcode的3.2是给我一个错误B / C我为我的@property没有匹配伊娃)。添加@dynamic解决了该问题-现在可以编译并正常运行。谢谢!
pix0r

4
对不起,买这是完全错误的。@dynamic告诉访问器在运行时解析,除非在类或超类(不在其他位置)中声明了访问器。您可以阅读文档developer.apple.com/library/mac/documentation/cocoa/conceptual/...
user1447414

5
凯夫拉尔:不。在现代的ObjC中,@property既没有@synthesize@dynamic不会自动合成的项目。对于每个属性,_propertyName将创建带有下划线的ivar,例如适当的getter和setter。
Dave R

212

看一下这篇文章 ; 在“运行时提供的方法”标题下:

一些访问器是在运行时动态创建的,例如CoreData的NSManagedObject类中使用的某些访问器。如果要声明和使用这些情况下的属性,但又希望避免在编译时对方法丢失的警告,则可以使用@dynamic指令而不是@synthesize。

...

使用@dynamic指令实际上告诉编译器“不用担心,一种方法即将出现。”

@synthesize另一方面,该指令在编译时为您生成访问器方法(尽管如“混合的综合和自定义访问器”一节中所述,它很灵活,如果实现了两者,则不会为您生成方法)。


27
这是更正确的人。这个答案是唯一谈论运行时创建的方法的答案,这似乎比
最受

30

正如其他人所说,通常,您使用@synthesize来让编译器为您生成getter和/或设置,如果要自己编写,则使用@dynamic。

还没有提到另一个微妙之处:@synthesize 允许您自己提供getter或setter的实现。如果您只想为某些额外的逻辑实现getter,但让编译器生成setter(对于对象,编写自己通常要复杂一点),这将很有用。

但是,如果您确实为@synthesize访问器编写了一个实现,则该实现仍必须由实数字段支持(例如,如果编写,则-(int) getFoo();必须具有一个int foo;字段)。如果该值是由其他值产生的(例如,从其他字段计算得出),则必须使用@dynamic。


2
+1表示重要区别:@dynamic允许您为类接口中未定义且通过自省的变量创建访问器。
mahboudz

24
“并且,@dynamic如果您要自己编写”,如果您自己编写,则不要使用动态。@dynamic关闭编译器检查以确保您实现了它们。如果您自己实现它们,则确实希望编译器进行检查。
user102008 2011年


14

这是 @dynamic的示例

#import <Foundation/Foundation.h>

@interface Book : NSObject
{
   NSMutableDictionary *data;
}
@property (retain) NSString *title;
@property (retain) NSString *author;
@end

@implementation Book
@dynamic title, author;

- (id)init
{
    if ((self = [super init])) {
        data = [[NSMutableDictionary alloc] init];
        [data setObject:@"Tom Sawyer" forKey:@"title"];
        [data setObject:@"Mark Twain" forKey:@"author"];
    }
    return self;
}

- (void)dealloc
{
    [data release];
    [super dealloc];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    NSString *sel = NSStringFromSelector(selector);
    if ([sel rangeOfString:@"set"].location == 0) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    } else {
        return [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
 }

- (void)forwardInvocation:(NSInvocation *)invocation
{
    NSString *key = NSStringFromSelector([invocation selector]);
    if ([key rangeOfString:@"set"].location == 0) {
        key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
        NSString *obj;
        [invocation getArgument:&obj atIndex:2];
        [data setObject:obj forKey:key];
    } else {
        NSString *obj = [data objectForKey:key];
        [invocation setReturnValue:&obj];
    }
}

@end

int main(int argc, char **argv)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    Book *book = [[Book alloc] init];
    printf("%s is written by %s\n", [book.title UTF8String], [book.author UTF8String]);
    book.title = @"1984";
    book.author = @"George Orwell";
    printf("%s is written by %s\n", [book.title UTF8String], [book.author UTF8String]);

   [book release];
   [pool release];
   return 0;
}


6

要补充的一件事是,如果将一个属性声明为@dynamic,它将不占用内存(我已使用分配工具确认)。结果是您可以在类类别中声明属性。


如果我重写类别中的属性设置器并使之动态,是否可以确保在运行时使用替代而不是父类的设置器?来自苹果公司的文档:“如果在类别中声明的方法的名称与原始类中的方法相同……对于在运行时使用哪种方法实现,其行为是不确定的。”
戴维·詹姆斯

不,我认为该行为仍未定义。使类别中的属性动态化不会更改属性设置器方法的运行时优先级。
曾颖培2015年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.