当属性的修饰符为readonly,表示该属性为只读,那么能否修改这个属性的值呢?
外部通过KVC设置与禁止KVC设置
如果你熟悉KVC的话,那你一定知道,如果不做任何设置的话,就算属性修饰符为readonly,也是可以进行设置的。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@end
NS_ASSUME_NONNULL_END
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
NSLog(@"before name:%@", p.name);
[p setValue:@"hui" forKey:@"name"];
NSLog(@"after name:%@", p.name);
}
@end
打印结果:
2019-07-03 09:18:10.906643+0800 KVODemo[1207:18379] before name:(null)
2019-07-03 09:18:10.906844+0800 KVODemo[1207:18379] after name:hui
说明就算属性的修饰符为readonly,通过KVC也是可以进行属性赋值。
那要怎么样才能连KVC都无法进行赋值?
前提,只读的属性是不会自动生成setter方法,并且开发者并没有实现setter方法。
需要了解一下,KVC的内部赋值深层次原理。
可以知道,调用setValue:forKey:的时候,会优先调用-set<Key>:方法。如果没有实现的话,就会检查
+ (BOOL)accessInstanceVariablesDirectly;
如果此类方法被重写,然后返回值为NO(默认返回的是YES),那么将不会再找
_<key>, _is<Key>, <key>, is<Key>这些成员变量,直接就调用
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
这样就可以完全禁止外部对只读属性进行修改。
#import "Person.h"
@implementation Person
+ (BOOL)accessInstanceVariablesDirectly
{
return NO;
}
@end
结果程序闪退了
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x600002c58330> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name.'
结论:
1、在类内部不要给只读属性主动添加setter方法;
2、重写+(BOOL)accessInstanceVariablesDirectly 函数并返回NO。
这样处理完后,外部就无法修改只读属性。
内部设置只读属性的几种方法
只读属性在.m文件中是不能通过点语法进行直接赋值。
会直接提示
Assignment to readonly property
- 通过给成员变量赋值 _<key>
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@implementation Person
- (instancetype)init
{
self = [super init];
if ( self ) {
_name = @"hui";
}
return self;
}
@end
- 主动生成setter方法,在setter方法里面赋值
#import "Person.h"
@implementation Person
- (instancetype)init
{
self = [super init];
if ( self ) {
self.name = @"hui";
}
return self;
}
- (void)setName:(NSString * _Nonnull)name
{
_name = name;
}
@end
- 类扩展添加相同属性
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@interface Person()
//加不加readwrite都可以,因为默认属性就是readwrite
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
- (instancetype)init
{
self = [super init];
if ( self ) {
self.name = @"hui";
}
return self;
}
@end
over!
网友评论