@synthesize和@dynamic

1. @property 


1. iOS6以后出来的关键词。


2. @property name:指示编译器自动合成setter和getter方法,编译器会自动给你生成setter(setter方法名即setName)和getter(而getter方法名即name)方法的声明以及实现还有一个以_name 的成员变量(name是你属性定义的变量名字),因此省去实例变量和属性重复输入的麻烦。


3.@property后面的关键字,这些属性修饰符大致分为四类:


(1)可变性 (Mutability)

- readonly,只生成getter方法,没有setter方法

- readwrite,是默认的


(2)内存管理 (Memory management)

- assign,是默认的,适用于内置类型(int, bool等)或者代理对象(delegate),不存在引用计数机制。

- retain,只适用于对象,不适用内置类型(int, bool等)。当使用setter方法时,将对象的引用计数加1。

- copy,使用setter方法时,拷贝一个对象,即在内存中产生新对象,而不是把原来的对象的引用计数加1。显然,复制出来的新对象的引用计数为1。


(3)并发性 (Concurrency)

- nonatomic,访问属性非原子性,一般单线程声明nonatomic,考虑到速度问题。多线程程序就不要使用nonatomic。

- atomic,访问属性原子性,与nonatomic相反。


(4)API控制 (API control)

- getter=newGetterName,指定新的getter方法名,一般重新改写BOOL实例变量的getter名。例如

@property (getter=isFinished) BOOL finished;  

setter=,指定新的setter方法名。


2. @synthesize 

1. @synthesize name:将实例变量_name名称换成name


这样写是为了区分成员变量_name和属性名称name,在.m里面使用的时候见到_name就知道是成员(实例)变量了,见到self.name就知道是属性了。另外,系统库中的所有类的声明部分都是这样写的, 总之一句话:区分成员变量名称和属性名称。使用@synthesize可以改变_name名称

2. @synthesize name = custom_name:将实例变量_name名称换成custom_name


注意:@synthesize不会影响@property产生的setter和getter方法的名称,@property和@synthesize不必成对出现。


3. setter和getter方法什么时候被调用?


例如,属性声明如下


@interface Person : NSObject
@property NSString *name; // atomic, assign, readwrite (default)
@end

有2种方法:

(1)显示调用(发送消息)

[instancePerson name]; // Call getter method
[instancePerson setName: @"niu"]; // Call setter method

(2)隐式调用(点语法)

NSString *name = instancePerson.firstName; // Call getter method
instancePerson.name = @"niu"; // Call setter method


如果在实例方法中直接使用实例变量,则不会调用对应的setter和getter方法,例如

NSString *myString = _name; // Won't call getter method


name = @"A string";// Won't call setter method

仅仅是对一个指针进行赋值。name仅仅是一个指针变量,记录了 @"A string"的地址。在这个过程中不会调用setter方法,不会调用setter方法,就和@property没有关系,从而,也和retainassign等属性没有关系。这种赋值方式就是一个简单的指针赋值。

显然直接使用实例变量会有风险,如内存泄露、循环引用等。最好访问实例变量,都通过@property产生的setter和getter方法。


4. @dynamic

如果不想编译器自作主张生成这些setter和getter方法,则使用@dynamic。


@dynamic (Xcode6以后省略这个了, 默认在 @implementation .m中添加这个@dynamic xxx; )告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告,然后由自己实现存取方法或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行的时动态生成子类属性。


有两种办法实现setter和getter方法:


1)自己提供setter和getter方法;


2)方法动态决议(DynamicMethod Resolution)。


举个简单例子,如下


#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;  
} /


运行该程序,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 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;  
} // mai


对于第二种方法,在运行时决定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 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  



猜你喜欢

转载自blog.csdn.net/shubinniu/article/details/53260984
今日推荐