1. The difference between @dynamic and @synthesize
@property has two corresponding words, one is @synthesize and the other is @dynamic. If neither @synthesize nor @dynamic is written, the default is @syntheszie var = _var;
The semantics of @synthesize is that if you do not manually implement the setter method and getter method, the compiler will automatically add this for you Two methods.
@dynamic tells the compiler that the setter and getter methods of the properties are implemented by the user themselves and are not automatically generated. (Of course, for readonly properties, you only need to provide a getter). If a property is declared as @dynamic var, and you do not provide @setter methods and @getter methods, there will be no problem when compiling, but when the program runs to instance.var = someVar, the program will crash due to the lack of setter methods; or when When running to someVar = var, the lack of getter method will also cause a crash. There is no problem at compile time, and the corresponding method is executed at runtime. This is the so-called dynamic binding.
2. Implement @dynamic access methods through private variables
1) Book.h
#import
#import
@interface Book :NSObject
{
@private
__strong NSString *_name;
__strong NSString *_author;
}
@property(nonatomic, copy ) NSString *name;
@property (nonatomic, copy) NSString *author;
@property(nonatomic, copy) NSString*version;
@end
2) Book.m
#import "Book.h"
@implementation Book
@dynamic name;
@dynamicauthor;
@synthesizeversion = _version;
- (id)init
{
self = [super init];
if(self)
{
}
return self;
}
- (NSString *)name
{
if(nil == _name)
{
_name = @"you forgot inputbook name" } p", _name }
Return _author; As can be seen from the above code , use @ After dynamic, you can access a private variable in the access method to assign or get a value. And @synthesize directly uses @synthesize var = _var; to directly equate attributes and private variables. This is the difference in writing form between the two.
3. Implement @dynamic access method through message forwarding
If @dynamic var = _var is used for an attribute, the compiler will report an error immediately. In this way, you cannot use _var in the setter method and getter method of var like @synthesize. Of course, you cannot write the following setter method and getter method
- (void)setVar:(id)newVar
{
self.var = newVar;
}
- (void)var
{
return self.var;
}
These two methods call themselves, which will cause an infinite loop and directly lead to the program collapse.
Here is a way to use the message forwarding mechanism to implement @dynamic setter and getter methods.
First code:
1) Book.h
#import
@interface Book: NSObject
{
@private
NSMutableDictionary *_proper tiesDict;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString*author;
@property (nonatomic, copy) NSString*version; ) Book.m
#import "Book.h"
@implementation Book
@dynamic name; // Cannot be written as name = _name; otherwise the compiler will report an error immediately
@dynamic author;
@synthesizeversion;
- (id)init
{
self = [super init];
if(self)
{
_propertiesDict = [[NSMutableDictionary alloc] init];
}
return self;
}
- (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:&objatIndex:2];
[_propertiesDict setObject:obj forKey:key];
}
else
{
NSString *obj = [_propertiesDict objectForKey:key];
[invocation setReturnValue:&obj];
}
}
@end
3)main.m
#import
#import "Book.h"
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Book *book = [[Book alloc] init];
book.name = @"c++ primer";
book.author = @"Stanley B.Lippman";
book.version = @"5.0";
NSLog(@"%@", book.name);
NSLog(@"%@", book.author);
NSLog(@"%@", book.version);
}
return 0;
}
程序分析:
1)在给程序添加消息转发功能以前,必须覆盖两个方法,即methodSignatureForSelector:和forwardInvocation:。methodSignatureForSelector:的作用在于为另一个类实现的消息创建一个有效的方法签名。forwardInvocation:将选择器转发给一个真正实现了该消息的对象。
2)Objective-C中的方法默认被隐藏了两个参数:self和_cmd。self指向对象本身,_cmd指向方法本身。举两个例子来说明:
例一:- (NSString *)name
这个方法实际上有两个参数:self和_cmd。
例二:- (void)setValue:(int)val
这个方法实际上有三个参数:self, _cmd和val。
被指定为动态实现的方法的参数类型有如下的要求:
A.第一个参数类型必须是id(就是self的类型)
B.第二个参数类型必须是SEL(就是_cmd的类型)
C.从第三个参数起,可以按照原方法的参数类型定义。举两个例子来说明:
例一:setHeight:(CGFloat)height中的参数height是浮点型的,所以第三个参数类型就是f。
例二:再比如setName:(NSString *)name中的参数name是字符串类型的,所以第三个参数类型就是@
3)在main.m中有一句代码是book.name = @"c++ primer";程序运行到这里时,会去Book.m中寻找setName:这个赋值方法。但是Book.m里并没有这个方法,于是程序进入methodSignatureForSelector:中进行消息转发。执行完之后,以"v@:@"作为方法签名类型返回。
What is v@:@ here? In fact, the first character v here represents that the return type of the function is void. The next three characters can be known by referring to the explanation in 2) above. They are the types of the three parameters self, _cmd, name, id, SEL, NSString.
Then the program enters the forwardInvocation method. The obtained key is the method name setName:, and then use [invocationgetArgument:&objatIndex:2]; to obtain the parameter value, here is "c++ primer". Why does the index here need to be 2? As analyzed previously, the 0th parameter is self, the 1st parameter is _cmd, and the 2nd parameter is the parameter behind the method.
Finally, use a variable dictionary to assign values. This completes the entire setter process.
4) There is a code in main.m which is NSLog(@"%@", book.name);. When the program runs here, it will go to Book.m to find the value method of name. However, there is no such value method in Book.m, so the program enters methodSignatureForSelector: to forward the message. After execution, "@@:" is returned as the method signature type. Here the first character @ represents the function return type NSString, the second character @ represents the type id of self, and the third character: represents the type SEL of _cmd.
Then the program enters the forwardInvocation method. The obtained key is the method name name. Finally, the corresponding value is obtained from the dictionary based on this key, thus completing the entire getter process.
5) Note that during the process of debugging the code, we found that only the assignment and value of name and author enter the two methods methodSignatureForSelector: and forwardInvocation:. There is also an attribute version, which does not enter the two methods methodSignatureForSelector: and forwardInvocation:. This is because the version attribute is marked as @synthesize, and the compiler automatically adds the setVersion and version methods, so there is no need to forward the message.
4. The use of @dynamic in subclasses of NSManagedObject
The most commonly used use of @dynamic is in NSManagedObject. At this time, there is no need to display the programming setter and getter methods. The reason is: @dynamic tells the compiler not to do any processing, allowing the compilation to pass. Its getter and setter methods will be dynamically created at runtime, and the Core Data framework will generate access methods for such properties.