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'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'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).