工作生活iOS

iOS - 属性不被外部篡改

2019-07-03  本文已影响0人  厦门_小灰灰

当属性的修饰符为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
  1. 通过给成员变量赋值 _<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
  1. 主动生成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
  1. 类扩展添加相同属性
#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!

上一篇下一篇

猜你喜欢

热点阅读