How Not to Crash #2: Mutation Exceptions 可变异常(不要枚举可变的集合)

本文探讨了在软件开发中使用可变集合所带来的潜在问题,特别是当这些集合被公开为API的一部分时。作者建议使用不可变集合或者在返回可变集合时提供其副本以减少错误发生的可能性。
How Not to Crash #2: Mutation Exceptions 可变异常

How Not to Crash #2: Mutation Exceptions 可变异常

problem

You get a collection from somewhere and enumerate it — and then you get an error about the collection being mutated as it was being enumerated. (在遍历可变数组的时候,数组被修改了.)The app crashes.

solove

You can avoid this unhappy fate with one simple trick: don’t enumerate mutable collections.(不要枚举可变集合)

Disagree with me

You might hold the reasonable position that the real answer is not to mutate a mutable collection while enumerating it. You should have enough understanding of your app to be 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 that future changes (by you or somebody else) introduce a crash.

Mutable collections should not be part of public API(可变集合不应该是公开API的一部分)

It should be extremely rare — or, better, never — that an object has a public property that is a mutable collection. Mutable collections should be internal to the object.(可变集合最好,尽可能的不要作为一对象的公开属性.可变的集合应该作为一个内部的变量.)

(Furthermore, as much as possible, public collections should be read-only. This isn’t always possible, of course.[公开的集合属性应该是只读的,尽可能做到这点])

潜在问题的代码

Now, it’s entirely likely that an object has a public collection that 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;

}

  • (NSArray *)operations {

    return self.mutableOperations;

That’s perfectly legal code: because mutableOperations is an NSMutableArray, it’s also an NSArray. (I did it this way for years. I thought to myself, “Hey, I’m a grownup. I can handle it.” But what I didn’t realize was that grownup developers write code to make errors less likely.)

Properties specified as immutable should be immutable in fact

In the above example, you’re advertising operations as an array that can be enumerated safely at any time. Another person — or you yourself, looking at this in six months — won’t necessarily realize that really you’re getting back a mutable array that can’t necessarily be safely enumerated.

改进后的代码

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 admit that I don’t always do that. It would have the advantage of making it completely clear to the user of the API what’s going on.)

性能问题

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 are fast and use a non-weird amount of memory. And I’ve never, ever found this to be a problem. If your app has performance or memory use issues, the problem is something else, 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(从Bug中得到的教训)

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 did a thing that triggered a mutation in layoutManagers, and the app crashed.

The answer: enumerate a copy of layoutManagers. Problem solved.

There’s a general point: if you’re getting a collection from code that isn’t yours, it doesn’t hurt to be defensive and enumerate a copy.

原文和参考译文

下载方式:https://renmaiwang.cn/s/t0445 在时序发生器设计实验中,如何达成T4至T1的生成? 时序发生器的构建可以通过运用一个4位循环移位寄存器来达成T4至T1的输出。 具体而言:- **CLR(清除)**: 作为全局清零信号,当CLR呈现低电平状态时,所有输出(涵盖T1至T4)皆会被清除。 - **STOP**: 在T4脉冲的下降沿时刻,若STOP信号处于低电平状态,则T1至T4会被重置。 - **启动流程**: 当启动信号START处于高电平,并且STOP为高电平时,移位寄存器将在每个时钟的上升沿向左移动一位。 移位寄存器的输出端对应了T4、T3、T2、T1。 #### 2. 时序发生器如何调控T1至T4的波形形态? 时序发生器通过以下几个信号调控T1至T4的波形形态:- **CLR**: 当CLR处于低电平状态时,所有输出均会被清零。 - **STOP**: 若STOP信号为低电平,且在T4脉冲的下降沿时刻,所有输出同样会被清零。 - **START**: 在START信号有效(通常为高电平),并且STOP为高电平时,移位寄存器启动,从而产生环形脉冲输出。 ### 微程序控制器实验#### 3. 微程序控制器实验中的四条机器指令及其对应的微程序段指定的机器指令及其关联的微程序段如下:- **NOP**: 00- **R0->B**: 04- **A+B->R0**: 05- **P<1>**: 30- **IN->R0**: 32- **R0->OUT**: 33- **HLT**: 35#### 4. 微程序段中的微操作/微命令序列针对每条微指令,其对应的微操作或微命令序列如下:- **IN->R0**: 输入(IN)单元的数据被...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值