属性只是一个声明,它允许 setter、getter 和点语法访问器(接口变量隐藏)。
它本身绝对没有任何作用,但允许您使用-[myInstance myProperty]
来获取变量或使用-[myInstance setMyProperty:]
来设置它(是的,方法名称自动分配给-setProperty:
and -property
)。
当声明一个属性时,你有三个类别——线程锁定、访问控制和内存管理。您只能为每个类别选择一个修饰符,如果您不选择一个,它会自动自动分配给一个。
@property (<thread locking>, <access control>, <memory management>) id property;
第一类可以是atomic
或nonatomic
。修饰符强制变量上的atomic
@synchronized(myInstance) 锁定,以允许线程安全。nonatomic
不使用同步块,并且不是线程安全的。如果您两者都不使用,它会自动设置为atomic
。
第二类可以是readonly
或readwrite
。readwrite
修饰符也允许修改属性,并允许自动生成 -setProperty: 方法。使用readonly
修饰符时,不能使用该-setProperty:
方法。您必须使用对象内部的内部变量来直接设置变量。
第三类可以是assign
、retain
和copy
。修饰符意味着将assign
内部对象指针设置为传递给-setProperty:
消息的指针。修饰符分配传递的retain
指针并将 a 传递-retain
给对象。
修饰符直接克隆对象-copy
指向内存中新地址的新对象的新指针。这通过调用传递的对象将内部对象指针设置为传递对象的副本-copy
。默认修饰符是assign
,如果您没有在对象上设置内存管理类别修饰符,编译器会警告您 - 因为对象assign
上的修饰符不受欢迎(除非明确声明)。
有关 -copy 的示例,请查看以下内容:
- (void)setProperty:(GXMyObject *)property {
// This points to the original passed object.
GXMyObject *original = property;
// This points to a copy of the passed object.
CGMyObject *copied = [property copy];
// This points to yet another copy of the passed object-
// Independent of the other copies and original.
_property = [property copy];
// The anotherProperty is now different on this copy
// than on the original and the other copies.
_property.anotherProperty = 4;
// This will prove that they are all individual objects.
NSLog(@"%p, %p, %p", original, copied, _property);
}
有一个可选的方法名称声明修饰符,用法如下:getter = myCustomPropertyGetter
and setter = myCustomPropertySetter:
(setter 方法名称末尾的冒号:
是必需的,因为它表示必须传递参数)。
后半部分是属性合成器或动态化器。一旦声明了一个属性(例如,myView
),如下所示:
@property (nonatomic, retain) NSView *myView;
您可以:自己定义 setter 和 getter;@synthesize
设置器和获取器;@dynamic
该属性表示它存在于类别或主类中,或者可能在运行时添加(请注意,这不是一个有趣的想法,并且可能导致不良的运行时异常)。
第一个示例,自己编写方法如下所示:
// In Apple's LLVM 3.1 Compiler, instance variables can be added
// within {} below the @implementation as well as the @interface,
// and in private categories (@interface GXMyClass ()) like before.
@implementation GXMyClass {
// The internal object pointer is prefixed with an _ to avoid name confusions.
NSView *_myView;
}
- (NSView *)myView {
return _myView;
}
- (void)setMyView:(NSView *)myView {
_myView = [myView retain];
}
@end
第二个例子是使用@synthesize
指令自动合成它:
@implementation GXMyClass
// In the new Apple LLVM 3.1 Clang compiler, the = operator when used
// next to the @synthesize directive declares an internal private
// variable and automatically sets to that variable.
@synthesize myView = _myView;
// The internal variable name is now myOtherView, because we did not use the
// = operator to assign an internal variable name to the property.
@synthesize myOtherView;
@end
在最后一个示例中,可能是最令人困惑的,因为它需要使用 @dynamic 指令,您需要添加一些类别或运行时方法:
@interface GXMyClass (InternalMethods)
@end
@implementation GXMyClass
// The = assignment operator does not work here.
@dynamic myView;
@end
@implementation GXMyClass (InternalMethods)
- (NSView *)myView {
return [self methodThatReturnsAnNSView];
}
- (void)setMyView:(NSView *)myView {
[self methodThatAcceptsAnNSViewArgument:myView];
}
@end
该@property
声明要求存在上述三个声明之一 - 它自己不做任何事情。然而,它允许的是点语法访问器(用于设置和获取属性的类似 Java 的访问器)。
例如,@property (copy) NSString *myName;
可以使用 访问-[myObject myName]
和设置使用-[myObject setMyName:]
。
现在可以设置 usingmyObjectInstance.myName = @"Bob";
和 get using myObjectInstance.myName
。利用上述所有概念,可以创建一个像这样的对象:
// The GXBufferQueue is a queue which buffers all requests, till they are read
// asynchronously later. The backing store is an NSMutableArray to which all
// buffer writes are appended to, and from which the first object is pulled and
// returned when the buffer is read to.
@interface GXBufferQueue
@property (nonatomic, readwrite, copy, setter = write:, getter = read) id buffer;
+ (GXBufferQueue *)queue;
@end
@implementation GXBufferQueue {
// This queue is an internal array and is 'tacked on' to the @implementation
// so no others can see it, and it can be marked @private so subclasses cannot
// use it. It is also good code practice to have @interfaces composed of only
// @properties, setters, and getters, rather than expose internal variables.
NSMutableArray *_internalQueue;
}
+ (GXBufferQueue *)queue {
return [[[GXBufferQueue alloc] init] autorelease];
}
- (id)init {
if((self = [super init])) {
_internalQueue = [[NSMutableArray alloc] init];
}
}
- (void)write:(id)buffer {
[_internalQueue addObject:buffer];
}
- (id)read {
if(!(_internalQueue.count > 0)) return nil;
id buffer = [_internalQueue objectAtIndex:0];
[_internalQueue removeObjectAtIndex:0];
return buffer;
}
@end
注意:此代码未经测试。现在你有了一个 GXBufferQueue,下面所有的工作:
GXBufferQueue *queue = [GXBufferQueue queue];
// Option One: using the traditional message syntax:
[queue write:@"This will be now added to the buffer."];
NSLog(@"Now the string written to the queue will be read \
and removed from the queue, like a stack pop. ", [queue read]);
// Option Two: using the new dot-syntax accessors:
queue.buffer = @"As clunky as this looks, it works the same as above.";
NSLog(@"These lines work just the same as the ones above: ", queue.buffer);
正如你所看到的,属性有很多可能,而且它们可以做的不仅仅是变量声明。如果有任何问题或社区希望我在帖子中添加/纠正的任何内容,请发表评论!:D