How to write a correct readonly property?
I have 2 questions about how to make a correct readonly property in Objective-C 2.0+.
Here is my or开发者_运维技巧iginal approach, let's call it solution 1:
@interface ClassA{
@private
NSMutableArray *a_;
}
// NOTE: no retain
@property (nonatomic, readonly) NSMutableArray *a;
@end
///////////////////////////////////////
@implementation ClassA
@synthesize a = a_;
- (NSMutableArray *)a{
if(nil == a_){
a_ = [[NSMutableArray alloc] array];
}
// Potential leak warning on the following line.
return a_;
}
- (void)dealloc{
// I released the object here, I think this should be safe.
[a_ release];
[super dealloc];
@end
When I compile and analyze it, the system report a warning like this: "a potential leak at 'return a_'".
Then I read the document of Objective-C again and find another approach as below. Let's call it solution 2.
@interface ClassB{
@private
NSMutableArray *a_;
}
// NOTE: make it retain+readonly
@property (nonatomic, readonly, retain) NSMutableArray *a;
@end
///////////////////////////////////////
// Add a private category
@interface ClassB ()
// reset the property to readwrite
@property (nonatomic, readwrite, retain) NSMutableArray *a;
@end
//////
@implementation ClassB
@synthesize a = a_;
- (id)init{
if(self = [super init]){
// NOTE: set the value as we use property normally.
self.a = [NSMutableArray array];
}
return self;
}
- (void)dealloc{
self.a = nil;
[super dealloc];
@end
Now, here are my questions:
- Is it possible to use solution 1 and get rid of 'potential leak'?
- Does solution 2 the common solution?
Thank you guys!
-- Tonny
As requested, I'm reproducing my comment as an answer:
[[NSMutableArray alloc] array]
should give you a compiler warning, and it will definitely crash. You want [[NSMutableArray alloc] init]
.
Honestly, I find it easier to just use "private" read-write properties and not fuss with the ivars at all:
MyClass.h
@interface MyClass : NSObject
@property (nonatomic, copy, readonly) NSArray * someArray; // Public
@end
MyClass.m
@interface MyClass () // Class extension
@property (nonatomic, copy, readwrite) NSArray * someArray; // "Private"
@end
@implementation MyClass
@synthesize someArray = someArray_;
- (id)init
{
self = [super init];
if (self != nil)
{
self.someArray = ...; // Array initialization
}
return self;
}
- (void)dealloc
{
[someArray_ release];
[super dealloc];
}
@end
No ivars needed! The modern runtime will automatically synthesize them. Your property is read-only from the outside (i.e., other classes), but internally, you've redeclared the property as read-write, so you can leverage the convenience of synthesized property accessors.
(Of course, I still declare an explicit ivar synthesis—in this example, someArray_
— for use in -dealloc
as there are good reasons not to use properties in -dealloc
and possibly in -init
.)
Generally if you now the value of a read only property will be ahead of time, it's good to set it up in the init method.
I'm not sure if this would cause a leak warning but I would do something like:
@interface ClassA{
@private
NSMutableArray a_;
}
// NOTE: no retain
@property (nonatomic, readonly) NSMutableArray a;
@end
@implementation ClassB
@synthesize a = a_;
- (id)init{
if(self = [super init]){
// NOTE: set the value as we use property normally.
a_ = [[NSMutableArray alloc] init];
}
return self;
}
- (NSMutableArray *)a
{
return a_;
}
- (void)dealloc{
[a_ release];
[super dealloc];
}
@end
EDITED:
Fixed a_ assignment.
精彩评论