这是一个好的(类似 Cocoa 的、Apple 认可的)模型类吗?
我已经使用 Objective-C 一段时间了,但我并没有很好地遵循 Apple 的指导方针。最近我读了 Cocoa 设计模式 和 模型对象实现指南,我正在尝试做一些非常简单的事情,但是做得很好。
我是否错过了任何主要概念?请不要提及 self = [super init]
; SO 已经对此进行了多次介绍。不过,请随意批评我的#pragma mark
!
#import "IRTileset.h"
#import "IRTileTemplate.h"
@interface IRTileset () //No longer lists protocols because of Felixyz
@property (retain) NSMutableArray* tileTemplates; //Added because of TechZen
@end
#pragma mark -
@implementation IRTileset
#pragma mark -
#pragma mark Initialization
- (IRTileset*)init
{
if (![super init])
{
return nil;
}
tileTemplates = [NSMutableArray new];
return self;
}
- (void)dealloc
{
[tileTemplates release];
[uniqueID release]; //Added because of Felixyz (and because OOPS. Gosh.)
[super dealloc]; //Moved from beginning to end because of Abizern
}
#pragma mark -
#pragma mark Copying/Archiving
- (IRTileset*)copyWithZone:(NSZone*)zone
{
IRTileset* copy = [IRTileset new];
[copy setTileTemplates:tileTemplates]; //No longer insertTileTemplates: because of Peter Hosey
[copy setUniqueID:uniqueID];
return copy; //No longer [copy autorelease] because of Jared P
}
- (void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:uniqueID forKey:@"uniqueID"];
[encoder encodeObject:tileTemplates forKey:@"tileTemplates"];
}
- (IRTileset*)initWithCoder:(NSCoder*)decoder
{
[self init];
[self setUniqueID:[decoder decodeObjectForKey:@"uniqueID"]];
[self setTileTemplates:[decoder decodeObjectForKey:@"tileTemplates"]]; //No longer insertTileTemplates: because of Peter Hosey
return self;
}
#pragma mark -
#pragma mark Public Accessors
@synthesize uniqueID;
@synthesize tileTemplates;
- (NSUInteger)countOfTileTemplates
{
return [tileTemplates count];
}
- (void)insertTileTemplates:(NSArray*)someTileTemplates atIndexes:(NSIndexSet*)indexes
{
[tileTemplates insertObjects:someTileTemplates atIndexes:indexes];
}
- (void)removeTileTemplatesAtIndexes:(NSIndexSet*)indexes
{
[tileTemplates removeObjectsAtIndexes:indexes];
}
//These are for later.
#pragma mark -
#pragma mark Private Accessors
#pragma mark -
#pragma mark Other
@end
(编辑:我已经做出了到目前为止建议的更改,并评论了哪些答案讨论它们,以防有人需要知道原因。)
I've been using Objective-C for a while, but I've not been following Apple's guidelines very well. Recently I read Cocoa Design Patterns and the Model Object Implementation Guide, and I'm trying to do some very simple things, but do them very well.
Have I missed any major concepts? Please don't mention self = [super init]
; that's been covered many times on SO already. Feel free to critique my #pragma mark
s though!
#import "IRTileset.h"
#import "IRTileTemplate.h"
@interface IRTileset () //No longer lists protocols because of Felixyz
@property (retain) NSMutableArray* tileTemplates; //Added because of TechZen
@end
#pragma mark -
@implementation IRTileset
#pragma mark -
#pragma mark Initialization
- (IRTileset*)init
{
if (![super init])
{
return nil;
}
tileTemplates = [NSMutableArray new];
return self;
}
- (void)dealloc
{
[tileTemplates release];
[uniqueID release]; //Added because of Felixyz (and because OOPS. Gosh.)
[super dealloc]; //Moved from beginning to end because of Abizern
}
#pragma mark -
#pragma mark Copying/Archiving
- (IRTileset*)copyWithZone:(NSZone*)zone
{
IRTileset* copy = [IRTileset new];
[copy setTileTemplates:tileTemplates]; //No longer insertTileTemplates: because of Peter Hosey
[copy setUniqueID:uniqueID];
return copy; //No longer [copy autorelease] because of Jared P
}
- (void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:uniqueID forKey:@"uniqueID"];
[encoder encodeObject:tileTemplates forKey:@"tileTemplates"];
}
- (IRTileset*)initWithCoder:(NSCoder*)decoder
{
[self init];
[self setUniqueID:[decoder decodeObjectForKey:@"uniqueID"]];
[self setTileTemplates:[decoder decodeObjectForKey:@"tileTemplates"]]; //No longer insertTileTemplates: because of Peter Hosey
return self;
}
#pragma mark -
#pragma mark Public Accessors
@synthesize uniqueID;
@synthesize tileTemplates;
- (NSUInteger)countOfTileTemplates
{
return [tileTemplates count];
}
- (void)insertTileTemplates:(NSArray*)someTileTemplates atIndexes:(NSIndexSet*)indexes
{
[tileTemplates insertObjects:someTileTemplates atIndexes:indexes];
}
- (void)removeTileTemplatesAtIndexes:(NSIndexSet*)indexes
{
[tileTemplates removeObjectsAtIndexes:indexes];
}
//These are for later.
#pragma mark -
#pragma mark Private Accessors
#pragma mark -
#pragma mark Other
@end
(Edit: I've made the changes suggested so far and commented which answers discuss them, in case anyone needs to know why.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
那么,为什么你不这样做呢?
initWithCoder:
也是如此:您应该使用[self init]
返回的对象,而不是假设它初始化了初始对象。正如 Abizern 在评论中所说,
[super dealloc]
应该放在最后。否则,您将访问已释放对象的实例变量。这里的返回类型应该是
id
,与NSCopying协议声明的返回类型匹配。您正在一个索引处插入零个或多个对象。使用范围创建索引集:位置 = 0,长度 =
tileTemplates
数组的计数。更好的是,只需分配给整个属性值:或者直接访问实例变量:(
请注意,在绕过属性访问器时,您必须自己进行
复制
,并且您是复制
代表副本对数组进行操作。)copyWithZone:
不应返回自动释放的对象。根据内存管理规则 ,copy
或copyWithZone:
的调用者拥有该副本,这意味着释放它是调用者的工作,而不是copyWithZone:
的工作。您可能还想实现单对象数组访问器:
当然,这是可选的。
So, why aren't you doing that?
The same goes for
initWithCoder:
: You should be using the object returned by[self init]
, not assuming that it initialized the initial object.As Abizern said in his comment,
[super dealloc]
should come last. Otherwise, you're accessing an instance variable of a deallocated object.The return type here should be
id
, matching the return type declared by the NSCopying protocol.You're inserting zero or more objects at one index. Create your index set with a range: location = 0, length = the count of the
tileTemplates
array. Better yet, just assign to the whole property value:Or access the instance variables directly:
(Note that you must do the
copy
yourself when bypassing property accessors, and that you arecopy
ing the array on behalf of the copy.)copyWithZone:
should not return an autoreleased object. According to the memory management rules, the caller ofcopy
orcopyWithZone:
owns the copy, which means it is the caller's job to release it, notcopyWithZone:
's.You may want to implement the single-object array accessors as well:
This is optional, of course.
不,你不应该。实现文件中声明的类扩展是一个扩展,因此您不必关心该类已声明遵循哪些协议。
我建议使用下划线标记实例变量的名称:
_tileTemplates
。 (纯粹主义者会告诉您添加下划线而不是添加下划线前缀;如果您害怕它们,就这样做。)不要使用
new
来实例化类。据我了解,从来不推荐这样做。在进行自己的释放之前不要调用[super dealloc]!在某些情况下这可能会导致崩溃。
我不确定
uniqueID
具有什么类型,但它不应该在dealloc
中发布吗?我永远不会将我的 @synthesize 指令放在文件中间(将它们直接放在“@implementation”下方)。
另外,由于对此类的作用没有明确的了解,
countOfTileTemplates
对我来说听起来不太好。也许只是“计数”就可以了,如果这个类是用来保存图块模板的,这一点很明确的话?No, you shouldn't. The class extension declared in the implementation file is an extension, so you don't have to care about which protocols the class has been declared to follow.
I would recommend to mark your instance variables' names with an underscore:
_tileTemplates
. (Purists will tell you to affix rather than prefix the underscore; do that if you're afraid of them.)Don't use
new
to instantiate classes. It's not recommended ever, as far as I understand.Don't call [super dealloc] before doing your own deallocation! This can cause a crash in certain circumstances.
I'm not sure what type
uniqueID
has, but shouldn't it also be released indealloc
?I would never put my @synthesize directives in the middle of a file (place them immediately below ´@implementation´).
Also, having no clear idea about the role of this class,
countOfTileTemplates
doesn't sound good to me. Maybe just ´count´ will do, if it's unambiguous that what this class does it to hold tile templates?它看起来相当不错,只是您的属性对外部对象的任意操作开放。理想情况下,数据应该仅由模型类本身直接操作,并且外部对象只能通过专用方法进行访问。
例如,如果某些外部代码调用此命令:
Boom,您已经丢失了所有数据,
该怎么办?
您应该将两个数据属性定义为只读。然后将访问器写入类内部,以管理它们在类实现中的保留。这样,外部对象更改tileTemplates的唯一方法是调用- insertTileTemplates:atIndexes:
和removeTileTemplatesAtIndexes:
方法。Edit01:
我想我第一次就搞砸了,所以让我再试一次。您应该按照以下模式设置数据模型类:
接口
因此在使用中它看起来像:
实现
您将像这样使用它:
现在该类具有防弹数据完整性。应用中的任何对象都可以设置和获取
publicString
字符串属性,但外部对象无法设置或获取private
属性。这意味着您可以安全地允许应用程序中的任何对象访问实例,而不必担心某些次要对象或方法中的粗心代码行会破坏所有内容。
It looks pretty good except you've left your properties open to arbitrary manipulation by external objects. Ideally, the data should be manipulated directly only by the model class itself and external objects should have access only via dedicated methods.
For example what if some external code calls this:
Boom, you've lost all your data.
You should define both the data properties as readonly. Then write accessors internal to the class that will managed their retention within the class implementation. This way the only way for an external object to change the tileTemplates is by calling the- insertTileTemplates:atIndexes:
andremoveTileTemplatesAtIndexes:
methods.Edit01:
I think I mangled it the first go, so let me try again. You should setup you data model class in the following pattern:
Interface
So in use it would look like:
Implementation
You would use it like so:
Now the class has bullet proof data integrity. Any object in your app can set and get the
publicString
string property but no external object can set or get theprivate
.This means you can safely allow access to the instance by any object in your app without worrying that a careless line of code in some minor object or method will trash everything.
两个小问题:
一种是 init 方法,(从风格上来说,我反对有 2 个不同的返回点,但这只是我),但是没有什么可以阻止 super 的 init 返回与自身或 nil 不同的对象,例如其类的不同对象,甚至只是完全是另一个物体。因此,
self = [super init]
通常是一个好主意,即使它在实践中可能起不了多大作用。其次是在 copyWithZone 方法中,您不复制tileTemplates,这可能是有意的,但通常是一个坏主意(除非它们是不可变的)。复制一个对象应该与分配一个新对象具有相同的效果,例如。保留计数为 1,因此不要自动释放它。另外,看起来您没有对该区域执行任何操作,因此您可能应该将其替换为类似
我发现的所有内容;除了副本的保留计数(这将导致稍后出现错误)之外,大部分只是我喜欢的东西,如果您更喜欢的话,您可以按照自己的方式进行。干得好
two minor nitpicks:
One is the init method, (where stylistically I'm against having 2 different return points but thats just me), however there's nothing stopping super's init from returning a different object than itself or nil, eg a different object of its class or even just another object altogether. For this reason,
self = [super init]
is generally a good idea, even if it probably won't do much in practice.Second is in the copyWithZone method, you don't copy the tileTemplates, which could be intentional but is generally a bad idea (unless they're immutable). Copying an object is supposed to have the same effect as allocing a fresh one, eg. retain count of 1, so don't autorelease it. Also, it doesn't look like you do anything with the zone, so you should probably replace it with something like
thats everything I found; with the exception of the retain count of copy (which WILL lead to bugs later on) mostly just stuff that I prefer, you can do it your way if you like it better. Good work