<title>How does not to Crash #2: Mutation Exceptions mutable exception</title>
How does not to Crash #2: Mutation Exceptions mutable exception problem Solove disagree with me Mutable collections should not being part of the public API (mutable collection should not be an open API) code for potential problems Improved code performance Issues Bonus Points:don ' t believe their lies (lessons learned from bugs)
How does not to Crash #2: Mutation Exceptions mutable exception
Problem
You get a collection from somewhere and enumerate It-and so you get an error about the collection being mutated as It was being enumerated. (when traversing a mutable array, the array is modified.) The app crashes.
Solove
You can avoid this unhappy fate and one simple Trick:don ' t enumerate mutable collections. (Do not enumerate mutable collections)
Disagree with me
You might hold the reasonable position, the real answer is isn't to mutate a mutable collection while enumerating it. You should has enough understanding of your app to being able to write code that safely enumerates a mutable collection.
Yes, you should. You absolutely should.
However:writing Crash-free code is about removing doubt. It's about minimizing the chances for errors, and minimizing the chance so future changes (by you or somebody else) intr Oduce a crash.
Mutable collections should not being part of the public API (mutable collection should not be an open API)
It should was extremely rare-or, better, never-that an object have a public property, which is a mutable collection. M Utable collections should is internal to the object. (mutable collections are best, as far as possible, not as an object's public property.) A mutable set should be used as an internal variable.)
(Furthermore, as much as possible, public collections should is read-only. This isn ' t always possible, of course. [Public collection properties should be read-only and do this as much as possible])
Code for potential problems
Now, it's entirely likely that's an object have a public collection which is internally a mutable collection. Think of an object that tracks operations. It might make the following public:
@property (nonatomic, readonly) Nsarray *operations;
and internally there ' s this:
@property (nonatomic) Nsmutablearray *mutableoperations; (Nsarray *) Operations {
return self.mutableoperations;
}
That's perfectly legal code:because Mutableoperations is an nsmutablearray, it's also an nsarray. (I did it the this to years. I thought to myself, "Hey, I ' m a grownup. I can handle it. " But what I didn ' t realize is that grownup developers write code to make errors less likely.)
Properties specified as immutable should is immutable in fact
In the above example, you ' re advertising operations as a array that can is enumerated safely at any time. Another person-or you yourself, looking at the six Months-won ' t necessarily realize that really you ' re getting BAC K a mutable array that can ' t necessarily is safely enumerated.
The improved Code
Here ' s the truth-in-advertising solution:
(Nsarray *) Operations {
return [self.mutableoperations copy];
}
(It wouldn ' t hurt to modify the property declaration also to make it clear that it's a copy, but I'm admit that I don ' t Alwa Ys do. It would has the advantage of making it completely clear to the user of the API "What's going on."
Performance issues
You might push back, citing performance or memory use issues or Both-but I ' ll admit Something:i ' m a performance junkie, And I spend an inappropriate amount of time in Instruments making sure things is fast and use a non-weird amount of memo Ry. And I ' ve never, ever found this to is a problem. If your app has performance or memory with issues, the problem is something else and not these copies. (Though you might consider using @autoreleasepool so that these copies don ' t last very long.)
Make the copy.
Bonus Points:don ' t believe their lies (lessons learned from bugs)
I recently fixed a mutation error when enumerating Nstextstorage Layoutmanagers:
@property (readonly, copy) Nsarray *layoutmanagers;
Obviously it ' s safe to enumerate. It ' s an nsarray, it says, and it's a copy. Cool. Enumerate away.
But it ' s a lie. In the debugger I found that it's an nsmutablearray (__nsarraym)-and that it's not a copy at all. It's Nstextstorage ' s _layoutmanagers instance variable, which is declared as an nsmutablearray.
and Some code in my enumeration block do a thing that triggered a mutation in layoutmanagers, and the app Crashe D.
The answer:enumerate a copy of Layoutmanagers. Problem solved.
There ' s a general point:if your ' re getting a collection from code this isn ' t yours, it doesn ' t hurt to be defensive and en Umerate a copy.
Original and reference translations
- Http://inessential.com/2015/05/16/how_not_to_crash_2_mutation_exceptions
- http://ifujun.com/ru-he-cai-neng-bu-beng-kui-2-mutation-exceptions/
How does not go to Crash #2: Mutation Exceptions mutable exception (do not enumerate mutable collections)