可以不在自定义init方法中调用[super init]吗?

时间:2013-02-16 19:31:51

标签: ios objective-c cocoa-touch nscoding

我有一个MKPolyline子类,我想实现NSCoding,即

@interface RSRoutePolyline : MKPolyline <NSCoding>

I asked a question on the best way to encode the c-array and got an excellent answer。但是,MKPolyline上没有定义init方法,即除了类方法polylineWithPoints:points之外没有其他方法可以提供数据。

这段代码我的评论是否合适?

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    MKMapPoint *points = self.points;
    NSUInteger pointCount = self.pointCount;

    NSData *pointData = [NSData dataWithBytes:points length:pointCount * sizeof(MKMapPoint)];
    [aCoder encodeObject:pointData forKey:@"points"];
    [aCoder encodeInteger:pointCount forKey:@"pointCount"];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    NSData* pointData = [aDecoder decodeObjectForKey:@"points"];
    NSUInteger pointCount = [aDecoder decodeIntegerForKey:@"pointCount"];

    // Edit here from @ughoavgfhw's comment
    MKMapPoint* points = (MKMapPoint*)[pointData bytes];

    // Is this line ok?
    self = (RSRoutePolyline*)[MKPolyline polylineWithPoints:points count:pointCount];

    return self;
}

3 个答案:

答案 0 :(得分:2)

如果不打电话给[super init]那就太脏了,而且我对好的编程想法并不好。没有自己超级称呼,它不是一个真正的子类;只是组合的混合,依赖于调用方便构造函数的副作用。说这个,我相信你描述的方法会运行正常,但它违背了良好的Objective-C编程及其惯例。

我建议将MKPolyLine用作MKPolyLine个实例,并使用类别添加您需要的额外铃声和口哨声。至于添加额外的实例变量等,您可以使用关联的对象。这个概念的介绍可以是found here,这个SO问题解决了它们与类别的使用:How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?

答案 1 :(得分:2)

您应该在NSObject的任何子类上调用init方法。由于MKPolyline是一个NSObject,你应该初始化它。

但MKPolyline没有方法也没有init。这是Objective C告诉你不能将它子类化。

相反,正如WDUK建议的那样,定义自己的类。它会跟踪您的列表点,并管理NSCoding以根据需要保存和恢复它们。

 @interface RSPolyline: NSObject<NSCoding>

 - (id) initWithPoints: (NSArray*) points;
 - (id) initWithCoder:(NSCoder *)aDecoder;
 - (void) encodeWithCoder:(NSCoder *)aCoder;

 - (MKPolyline*) polyLine;

 @end

您的类可以根据请求生成折线,如果性能有问题,可能会缓存结果。

通常,不要先进行继承。当你想扩展和改进一个类时,首先考虑组合。

答案 2 :(得分:1)

虽然通常允许在init方法中创建和返回不同的对象,但该行有三个问题(如下所述)。而不是这样,我建议覆盖pointspointCount属性,以便您可以返回存储在实例变量中的值,并在实例变量为空时调用那里的超级实现。然后,您的初始化程序只是设置这些实例变量,以便它们将被使用。

- (MKMapPoint *)points {
    if(myPointsIvar == NULL) return [super points];
    else return myPointsIvar;
}
// similarly for pointCount

第一个问题是你正在创建一个新对象,但是没有释放旧对象,这意味着你正在泄漏它。您应该将结果存储在另一个变量中,然后释放self,然后返回结果(您不需要将其存储在self中)。

其次,polylineWithPoints:count:返回一个自动释放的对象,但initWithCoder:应返回一个保留的对象。除非有其他保留,否则在您仍在使用时可以取消分配。

如果这些是唯一的问题,你可以这样解决:

MKPolyline *result = [MKPolyline polylineWithPoints:points count:pointCount];
[self release];
return [result retain];

但是,还有第三个问题不容易解决。 polylineWithPoints:count:不返回RSRoutePolyline对象,它返回的对象可能与您的子类的方法不兼容(例如,它可能不支持NSCoding)。确实无法解决此问题,因此您无法使用polylineWithPoints:count: