Is it ok not to invoke [super init] in a custom init method?

拟墨画扇 提交于 2019-12-23 02:55:17

问题


I have a MKPolyline subblass which I want to implement NSCoding, i.e.

@interface RSRoutePolyline : MKPolyline <NSCoding>

I asked a question on the best way to encode the c-array and got an excellent answer. However, there is no init method defined on MKPolyline, i.e. there is no other way to give it data other than its class method polylineWithPoints:points.

Is this code where my comment is ok?

- (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;
}

回答1:


You should call an init method on any subclass of NSObject. Since MKPolyline is an NSObject, you should init it.

But MKPolyline has no methods and no init. This is Objective C's was of telling you that you can't subclass it.

Instead, as WDUK suggested, define your own class. It keeps track of your list point points, and manages NSCoding to save and restore them as needed.

 @interface RSPolyline: NSObject<NSCoding>

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

 - (MKPolyline*) polyLine;

 @end

Your class can generate a polyline on request, perhaps caching the result if performance is an issue.

As a rule, don't reach for inheritance first. When you want to extend and improve a class, think first of composition.




回答2:


It's dirty not to call [super init], and it doesn't bode well with my idea of good programming. Without calling super yourself, it isn't a true subclass; just a bastardization of composition that relies on a side effect of calling a convenience constructor. Saying this, I believe your method described will work OK, but it goes against the grain of good Objective-C programming and its conventions.

What I would suggest is to use MKPolyLine as an MKPolyLine instance, and use a category to add the extra bells and whistles you need. As for adding extra instance variables and such, you can use associated objects. An introduction to this concept can be found here, and this SO question addresses the use of them with categories: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?




回答3:


While it is generally allowed to create and return a different object in an init method, there are three problems with that line (explained below). Instead of this, I would suggest overriding the points and pointCount properties so that you can return values stored in an instance variable, and call the super implementation there if the instance variable is empty. Then, your initializer just sets these instance variables so that they will be used.

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

The first problem is that you are creating a new object, but not releasing the old one, which means you are leaking it. You should store the result in a different variable, then release self, then return the result (you don't need to store it in self).

Second, polylineWithPoints:count: returns an autoreleased object, but initWithCoder: should return a retained one. Unless there is another retain on it, it could be deallocated while you are still using it.

If these were the only problems, you could solve both like this:

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

However, there is a third problem which cannot be solved so easily. polylineWithPoints:count: does not return a RSRoutePolyline object, and the object it returns may not be compatible with your subclass's methods (e.g. it probably won't support NSCoding). There really isn't a way to fix this, so you can't use polylineWithPoints:count:.



来源:https://stackoverflow.com/questions/14914265/is-it-ok-not-to-invoke-super-init-in-a-custom-init-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!