【iOS】为什么 readonly NSMutableArray 还能被修改?——Objective-C 中的“只读”迷思
在日常 Objective-C 开发中,我们常常会给属性加上 readonly 修饰符,认为这样就能防止外部修改它。
但是很多同学第一次遇到下面的情况时,都会一脸疑惑:
@property (nonatomic, strong, readonly) NSMutableArray<FBMutableBeacon *> *beacons; // 代码里竟然还能这么写 [self.beacons addObject:beacon]; [self.beacons removeObjectAtIndex:0];
不是 readonly 吗?为什么还能修改?
本文就来彻底解释这个问题。
1. readonly 的真正含义
在 Objective-C 中,readonly 的作用是 不生成 setter 方法,也就是说:
@property (nonatomic, strong, readonly) NSMutableArray *beacons;
等价于:
- (NSMutableArray *)beacons; // 只有 getter
因此你不能写:
self.beacons = [NSMutableArray array]; // ❌ 编译错误,没有 setter
这就是 readonly 的全部含义。
换句话说,它限制的是 重新赋值行为,而不是对象的修改。
2. 可变对象依然可变
再看一遍上面的例子:
[self.beacons addObject:@"BeaconA"]; // ✅ 合法 [self.beacons removeObjectAtIndex:0]; // ✅ 合法
原因很简单:
-
beacons指向的是一个NSMutableArray对象。 -
NSMutableArray本身就是可变的,提供了addObject:、removeObject:等方法。 -
调用这些方法修改的不是属性本身,而是数组内容。
所以,readonly 并不会阻止这种修改。
3. 常见误区
很多人会把 readonly 和 immutable(不可变对象) 混为一谈。
实际上它们完全不同:
-
readonly:只读属性,不能重新赋值,但对象本身是否可变要看类型。 -
NSArray:不可变对象,本身就没有修改方法。 -
NSMutableArray:可变对象,依然可以修改。
对比一下:
@property (nonatomic, strong, readonly) NSMutableArray *arr1; @property (nonatomic, strong, readonly) NSArray *arr2; [arr1 addObject:@"A"]; // ✅ 合法 [arr2 addObject:@"A"]; // ❌ 编译错误,NSArray 没有这个方法
4. 正确的设计方式
如果你希望外部真正无法修改数组,有几种推荐做法:
✅ 方法一:用不可变类型暴露
@property (nonatomic, strong, readonly) NSArray<FBMutableBeacon *> *beacons;
内部依然可以用 NSMutableArray 存储,但对外只暴露 NSArray,自然无法修改。
✅ 方法二:返回副本(copy)
- (NSArray<FBMutableBeacon *> *)beacons { return [_beacons copy]; // 返回不可变拷贝 }
这样即使内部是可变数组,外部拿到的也是独立的不可变对象。
✅ 方法三:提供受控接口
而不是直接暴露数组,提供明确的方法:
- (void)addBeacon:(FBMutableBeacon *)beacon; - (void)removeBeaconAtIndex:(NSUInteger)index;
这样可以完全控制外部对数据的修改方式。
5. 总结
-
readonly≠ 不可变 -
它只意味着属性不能被重新赋值,但并不保证对象本身不可变。
-
想要真正防止外部修改数组内容,应该:
-
使用
NSArray暴露 -
或者返回
copy -
或者提供受控接口
-
一句话点题:
Objective-C 中的 readonly,其实只是“只读引用”,而不是“只读对象”。
👉 这就是 readonly NSMutableArray 还能被修改的根本原因。

浙公网安备 33010602011771号