Re: Debugging insight needed for NSKeyedUnarchiver
Re: Debugging insight needed for NSKeyedUnarchiver
- Subject: Re: Debugging insight needed for NSKeyedUnarchiver
- From: Graham Cox <email@hidden>
- Date: Mon, 03 Mar 2014 20:47:14 +1100
On 3 Mar 2014, at 7:48 pm, Quincey Morris <email@hidden> wrote:
> What are you suggesting that these rules are? AFAIK, there isn’t any “rule” about whether an init method can return nil — it’s part of the API contract in each individual case, isn’t it?
The documentation for -init states:
"If the new object can’t be initialized, the method should return nil. In some cases, an init method might return a substitute object. You must therefore always use the object returned by init, and not the one returned by alloc or allocWithZone:, in subsequent code."
This has also been the subject of much discussion that I've seen over the years of how to actually do this, and what I do is to call [self autorelease]; then self = (either a new retained object or nil); return self;
This then gets further complicated by designated initializers and the like, but the basic idea remains.
- initWithCoder: is just the designated initializer used when dearchiving, so my reading of it is that it "inherits" the rules about returning nil or another object from -init.
The documentation for -initWithCoder: states (and this seems to be something added recently) that -initWithCoder: must return self. However it does not say anything about self being reassigned or set to nil like any other init method is permitted to do.
> IAC, I don’t think it’s exactly about whether initWithCoder returns nil. Surely it’s about the fact that decoding a NSArray can’t deal with a nil element, since it can’t be inserted into the array. Simply dropping nil elements doesn’t even seem like a possible strategy.
Why not? If I decode a list of objects into an array, it would be a very simple matter when looping over those objects to skip nil items. If the code uses -arrayWithObjects: instead, then that's a choice made by its designer, not the only possible way to do it. I would argue that it's definitely the wrong choice - it's making an assumption about how external code is operating.
Looking at it from the opposite point of view, if you have five objects in an archive but only the first one is invalid and can't be initialized, then why should it go on to drop the remaining four objects in that array? It certainly doesn't stop it from dearchiving further objects in a different array. Or, if the first two objects were fine, but number 3 was nil, then you end up with two objects in the array instead of four. How is that behaviour useful or consistent? What you get depends on the order of the items in the array, which seems to me unintentional - if there were a (documented) rule that says if any object is nil the whole array ends up empty then OK, but right now it contains the objects up to, but not following, the nil object.
> I think you have to assume that it’s your responsibility not to return nil when the object being decoded is part of a collection**. After all, it was certainly non-nil when it was originally archived.
Yes, but the discussion for -init says it's fine to return nil "if the new object can't be initialized". There are many good reasons why an object could be archived but when dearchiving it would be better to skip it, such as the case I am faced with.
> I’d also say there’s a parallel problem when *not* dealing with array elements. If in unarchiving an object (*not* in a collection) you return nil from ‘initWithCoder’ (under the assumption that the object wasn’t nil when archived), you’ll end up with a “hole” in your data model, and you won’t know about it.
I'm not sure about that. If your code deliberately does work to discontinue initialization, then you definitely know about it.
> Putting those two things together, it seems that simply returning nil from initWithCoder should *never* be done, which is the policy I’ve always followed.
If that's the conclusion, which I'm not yet convinced about, then it should be documented. I can't find anywhere that says that. It would also be a simple matter to enforce it - NSKeyedUnarchiver could throw an exception if an object was allocated but returned nil from -initWithCoder:. It does not do that, so it's obviously not considering it as an error. (This is distinct from having a nil value actually archived - in that case there is no object allocated).
--Graham
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden