Home  >  Article  >  Java  >  Introducing the usage of @dynamic

Introducing the usage of @dynamic

高洛峰
高洛峰Original
2016-12-13 09:18:512614browse

Introducing the usage of @dynamic

Objective-C 2.0 provides properties (@property), which allows the compiler to automatically generate setter and getter methods. If you don't want the compiler to generate these setter and getter methods on its own, use @dynamic. To give a simple example, as follows

#import <Foundation/Foundation.h>  
  
@interface Person : NSObject  
@property (copy) NSString *name;  
@end  
  
@implementation Person  
// @dynamic tells compiler don&#39;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

When running the program, Xcode will report an error "-[PersonsetName:]: unrecognized selector sent to instance 0x1001149d0". If @dynamic is commented out, everything is OK.

Due to the use of @dynamic here, we need to provide the setter and getter methods ourselves. There are generally two methods: 1) Provide setter and getter methods yourself, and manually rewrite the setter and getter methods automatically generated by the compiler; 2) Dynamic method resolution (DynamicMethod Resolution), provide corresponding implementations of setters and getters at runtime. C function.

For the first method, the instance variables need to be provided explicitly in the class, because @dynamic cannot provide instance variables to the implementation file (.m) like @synthesize.

#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&#39;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

For the second method, the resolveInstanceMethod: method provided by NSObject is used to determine the C function corresponding to the setter and getter implementation at runtime. Instance variables cannot be used directly in C functions. The ObjC object self needs to be converted into a structure in C. Therefore, the instance variables also need to be explicitly declared in the Person class and the access level is @public. In order to hide the instance variables, put the declaration in In 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

To summarize the above, the function of @dynamic is to prohibit the compiler from generating setter and getter methods for @property. There are two ways to implement setter and getter methods: 1) Provide setter and getter methods yourself; 2) Method dynamic resolution (DynamicMethod Resolution).

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn