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没有关系,从而,也和retain,assign等属性没有关系。这种赋值方式就是一个简单的指针赋值。
显然直接使用实例变量会有风险,如内存泄露、循环引用等。最好访问实例变量,都通过@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