I want to protect access to NSMutableArray in public interface
我想保护对公共接口中NSMutableArray的访问
I am trying to do this by defining property as NSArray in public interface and as NSMutableArray in private interface like this:
我试图通过在公共接口中将属性定义为NSArray并在私有接口中将NSMutableArray定义为:
@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end
@interface Order()
@property (readwrite, strong, nonatomic) NSMutableArray* comments;
@end
But this doesn't work - so I have to define property in public interface NSMutableArray instead:
但这不起作用 - 所以我必须在公共接口NSMutableArray中定义属性:
@interface Order
@property (strong, nonatomic) NSMutableArray* comments;
@end
The goal is to provide read-only access to comments for API clients and full access to methods like addObject:
in the implementation.
目标是为API客户端提供对注释的只读访问权限,并在实现中提供对addObject:等方法的完全访问权限。
So defining goal more clearer:
因此,定义目标更清晰:
- Client should have access to property as NSArray without ability to access mutation methods.
- Client should not have ability to update the comments to point to new value.
- The solution must be done without creating extra structures and array copying.
客户端应该可以作为NSArray访问属性,而无法访问变异方法。
客户端无法更新注释以指向新值。
必须在不创建额外结构和阵列复制的情况下完成解决方案。
So simply the question was if it's possible to make public definition of property more general (NSArray instead of NSMutableArray).
所以简单的问题是,是否可以使属性的公共定义更通用(NSArray而不是NSMutableArray)。
Is there any other clean way to reach the goal or I have to use NSMutableArray everywhere?
有没有其他干净的方法来达到目标或我必须在任何地方使用NSMutableArray?
RESOLUTION
After reviewing my original question and answers I realized that I would like to use more generic class NSArray in the public interface and NSMutableArray in the implementation - but it's just not possible for one property. So the answer is not possible.
在回顾了我原来的问题和答案之后,我意识到我想在公共接口中使用更多泛型类NSArray,在实现中使用NSMutableArray - 但这对于一个属性来说是不可能的。所以答案是不可能的。
So I will just use single property with NSMutableArray without any extra desired protection.
因此,我将使用NSMutableArray的单一属性,而不需要任何额外的保护。
But I will also pick the most appropriate answer it might help if you really prefer protection over simplicity and efficiency.
但是如果你真的更喜欢保护而不是简单和高效,我也会选择最合适的答案。
5 个解决方案
#1
13
You don't need a public property if all you want is to allow clients to read the array.
如果您只想让客户端读取数组,则不需要公共属性。
Just create an accessor method that returns a copy of your private mutable array:
只需创建一个访问器方法,该方法返回私有可变数组的副本:
@interface Order : NSObject
- (NSArray *)allComments;
@end
@implementation Order ()
@property (nonatomic, strong) NSMutableArray * comments;
@end
@implementation Order
@synthesize comments;
- (NSArray *)allComments
{
return [[self comments] copy];
}
@end
This pattern can be seen in e.g., NSView
: constraints
and subviews
are internally mutable, but only exposed for reading via a single method returning a nonmutable array.
这种模式可以在例如NSView中看到:约束和子视图在内部是可变的,但只能通过返回不可变数组的单个方法进行读取。
#2
3
One solution would be to declare the property as readonly as an NSArray
. Then in your implementation, create a separate, writable property based on NSMutableArray
.
一种解决方案是将属性声明为NSArray只读。然后在您的实现中,基于NSMutableArray创建一个单独的可写属性。
In the .h:
在.h:
@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end
In the .m:
在他们中:
@interface Order()
@property (strong, nonatomic) NSMutableArray* internalComments;
@end
Instead of synthesizing the readonly property, write:
不要合成只读属性,而是写:
- (NSArray *)comments {
return [self.internalComments copy];
}
In the .m you do everything with self.internalComments
.
在.m中,您可以使用self.internalComments完成所有操作。
#3
2
A far cleaner solution, if it's acceptable for your use case, would be to declare the property as an NSArray
, while backing it with an NSMutableArray
. The client could technically modify the array by simply casting it to a mutable one, but you are making it clear that doing so would be a bad idea.
一个更清晰的解决方案,如果你的用例可以接受的话,就是将属性声明为NSArray,同时用NSMutableArray支持它。客户端可以通过简单地将其转换为可变数组来从技术上修改数组,但是你明确表示这样做是个坏主意。
A @property
is simply a wrapper around two methods, a getter and a setter, which are typically backed by a storage variable. The simplest way to implement this solution would be:
@property只是两个方法的包装器,一个getter和一个setter,它们通常由一个存储变量支持。实现此解决方案的最简单方法是:
@interface Order : NSObject
{
NSMutableArray *_comments;
}
@property (readonly, strong, nonatomic) NSArray *comments;
- (void)addComment:(Comment *)comment;
@end
@implementation Order
@synthesize comments=_comments; // Can be omitted if you use Xcode 4.4+
- (void)addComment:(Comment *)comment
{
[_comments addObject:comment];
}
@end
If you want the user to be able to replace the entire array (order.comments = ...
), remove the readonly
attribute on the property and override the -setComments:
method:
如果您希望用户能够替换整个数组(order.comments = ...),请删除属性上的readonly属性并覆盖-setComments:方法:
- (void)setComments:(NSArray *)array
{
[_comments release]; // Can be omitted if you are using ARC
_comments = [array mutableCopy];
}
It's worth noting that since Objective-C is a dynamic language, it's not possible to fully prevent someone from accessing a variable, as you can interface directly with the runtime or call methods by their selector if you really want to poke around in things you're not supposed to. All you can really do is make it clear that doing so is a bad idea.
值得注意的是,由于Objective-C是一种动态语言,因此不可能完全阻止某人访问变量,因为你可以直接与运行时接口或者通过选择器调用方法,如果你真的想在你的东西中找到它们的话。不应该。你所能做的就是明确表示这样做是个坏主意。
#4
1
Two solutions:
- Return a non-mutable copy of the array - client gets all the comments in one go.
- Return the number of comments and individual comments.
返回数组的不可变副本 - 客户端一次性获取所有注释。
返回评论数量和个人评论。
Take your pick, there are arguments for both and it depends on what suits you:
请你选择,两者都有争议,这取决于你的选择:
@interface Order : NSObject
// solution one - return a copy
@property (readonly, strong, nonatomic) NSArray* comments;
// solution two - return individual comments
@property (readonly) NSUInteger commentCount;
- (id) comment:(NSUInteger)number;
@end
@interface Order()
// internal property - mutable
@property (readwrite, strong, nonatomic) NSMutableArray* privateComments;
@end
@implementation Order
// solution one - return a copy
- (NSArray *) comments { return [self.privateComments copy]; }
// solution two - return individual comments
- (NSUInteger) commentCount { return self.privateComents.count; }
- (id) comment:(NSUInteger)number { return self.privateComment[number]; }
@end
#5
0
If you don't want the client to be able to set this property at all, then declare it in the public interface as readonly
. If you don't want the client to be able to mutate the array that the client can read, then declare it as NSArray. Meanwhile, on your side you are readwrite
and copy
. By being readwrite
you can take a mutableCopy
(which will be an NSMutableArray), make your changes, and then set the property again; by being copy
you ensure that the client always sees an NSArray.
如果您不希望客户端根本无法设置此属性,则在公共接口中将其声明为readonly。如果您不希望客户端能够改变客户端可以读取的数组,则将其声明为NSArray。同时,在您身边,您可以进行读写和复制。通过读写,你可以使用mutableCopy(它将是一个NSMutableArray),进行更改,然后再次设置属性;通过复制,您可以确保客户端始终可以看到NSArray。
So, client:
NSArray* arr = order.comments; // ok
[arr addObject: @"ha"]; // no, can't
order.comments = someOtherArray; // no, can't
Inside Order:
NSMutableArray* marr = self.comments.mutableCopy;
[marr addObject: @"ha"];
self.comments = marr; // and it is magically turned back into an NSArray
#1
13
You don't need a public property if all you want is to allow clients to read the array.
如果您只想让客户端读取数组,则不需要公共属性。
Just create an accessor method that returns a copy of your private mutable array:
只需创建一个访问器方法,该方法返回私有可变数组的副本:
@interface Order : NSObject
- (NSArray *)allComments;
@end
@implementation Order ()
@property (nonatomic, strong) NSMutableArray * comments;
@end
@implementation Order
@synthesize comments;
- (NSArray *)allComments
{
return [[self comments] copy];
}
@end
This pattern can be seen in e.g., NSView
: constraints
and subviews
are internally mutable, but only exposed for reading via a single method returning a nonmutable array.
这种模式可以在例如NSView中看到:约束和子视图在内部是可变的,但只能通过返回不可变数组的单个方法进行读取。
#2
3
One solution would be to declare the property as readonly as an NSArray
. Then in your implementation, create a separate, writable property based on NSMutableArray
.
一种解决方案是将属性声明为NSArray只读。然后在您的实现中,基于NSMutableArray创建一个单独的可写属性。
In the .h:
在.h:
@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end
In the .m:
在他们中:
@interface Order()
@property (strong, nonatomic) NSMutableArray* internalComments;
@end
Instead of synthesizing the readonly property, write:
不要合成只读属性,而是写:
- (NSArray *)comments {
return [self.internalComments copy];
}
In the .m you do everything with self.internalComments
.
在.m中,您可以使用self.internalComments完成所有操作。
#3
2
A far cleaner solution, if it's acceptable for your use case, would be to declare the property as an NSArray
, while backing it with an NSMutableArray
. The client could technically modify the array by simply casting it to a mutable one, but you are making it clear that doing so would be a bad idea.
一个更清晰的解决方案,如果你的用例可以接受的话,就是将属性声明为NSArray,同时用NSMutableArray支持它。客户端可以通过简单地将其转换为可变数组来从技术上修改数组,但是你明确表示这样做是个坏主意。
A @property
is simply a wrapper around two methods, a getter and a setter, which are typically backed by a storage variable. The simplest way to implement this solution would be:
@property只是两个方法的包装器,一个getter和一个setter,它们通常由一个存储变量支持。实现此解决方案的最简单方法是:
@interface Order : NSObject
{
NSMutableArray *_comments;
}
@property (readonly, strong, nonatomic) NSArray *comments;
- (void)addComment:(Comment *)comment;
@end
@implementation Order
@synthesize comments=_comments; // Can be omitted if you use Xcode 4.4+
- (void)addComment:(Comment *)comment
{
[_comments addObject:comment];
}
@end
If you want the user to be able to replace the entire array (order.comments = ...
), remove the readonly
attribute on the property and override the -setComments:
method:
如果您希望用户能够替换整个数组(order.comments = ...),请删除属性上的readonly属性并覆盖-setComments:方法:
- (void)setComments:(NSArray *)array
{
[_comments release]; // Can be omitted if you are using ARC
_comments = [array mutableCopy];
}
It's worth noting that since Objective-C is a dynamic language, it's not possible to fully prevent someone from accessing a variable, as you can interface directly with the runtime or call methods by their selector if you really want to poke around in things you're not supposed to. All you can really do is make it clear that doing so is a bad idea.
值得注意的是,由于Objective-C是一种动态语言,因此不可能完全阻止某人访问变量,因为你可以直接与运行时接口或者通过选择器调用方法,如果你真的想在你的东西中找到它们的话。不应该。你所能做的就是明确表示这样做是个坏主意。
#4
1
Two solutions:
- Return a non-mutable copy of the array - client gets all the comments in one go.
- Return the number of comments and individual comments.
返回数组的不可变副本 - 客户端一次性获取所有注释。
返回评论数量和个人评论。
Take your pick, there are arguments for both and it depends on what suits you:
请你选择,两者都有争议,这取决于你的选择:
@interface Order : NSObject
// solution one - return a copy
@property (readonly, strong, nonatomic) NSArray* comments;
// solution two - return individual comments
@property (readonly) NSUInteger commentCount;
- (id) comment:(NSUInteger)number;
@end
@interface Order()
// internal property - mutable
@property (readwrite, strong, nonatomic) NSMutableArray* privateComments;
@end
@implementation Order
// solution one - return a copy
- (NSArray *) comments { return [self.privateComments copy]; }
// solution two - return individual comments
- (NSUInteger) commentCount { return self.privateComents.count; }
- (id) comment:(NSUInteger)number { return self.privateComment[number]; }
@end
#5
0
If you don't want the client to be able to set this property at all, then declare it in the public interface as readonly
. If you don't want the client to be able to mutate the array that the client can read, then declare it as NSArray. Meanwhile, on your side you are readwrite
and copy
. By being readwrite
you can take a mutableCopy
(which will be an NSMutableArray), make your changes, and then set the property again; by being copy
you ensure that the client always sees an NSArray.
如果您不希望客户端根本无法设置此属性,则在公共接口中将其声明为readonly。如果您不希望客户端能够改变客户端可以读取的数组,则将其声明为NSArray。同时,在您身边,您可以进行读写和复制。通过读写,你可以使用mutableCopy(它将是一个NSMutableArray),进行更改,然后再次设置属性;通过复制,您可以确保客户端始终可以看到NSArray。
So, client:
NSArray* arr = order.comments; // ok
[arr addObject: @"ha"]; // no, can't
order.comments = someOtherArray; // no, can't
Inside Order:
NSMutableArray* marr = self.comments.mutableCopy;
[marr addObject: @"ha"];
self.comments = marr; // and it is magically turned back into an NSArray