我们都知道iOS中,是不能通过点方法或者[]方法进行readonly属性的赋值的。如果你执意要调用,那么Xcode会报错,readonly属性是不支持赋值操作的。
那么,我们有其他办法给这个readonly的属性赋值吗?
这里先写结论,可以。通过KVC的setValue:forKey方法可以给这个属性赋值。
这里就涉及到了KVC的知识点了,先说一下KVC的setValue:forKey方法的基本原理吧。
第一步:会查找对象的类中是否有满足set<key>格式的方法。如果有,那么直接调用,进行赋值操作。如果没有走下一步。
第二步:没有找到直接名称的方法,那么会调用对象的所属类的类方法+(BOOL)accessInstanceVariablesDirectly。默认这个方法是返回YES的。个人通过方法以及名称理解这个方法返回值来判断是否允许直接访问对象对应的实例变量。默认返回YES,允许访问。
第三步:如果上边允许访问,那么开始查找对象中key对应的实例变量,如果查找到直接改变key对应的value值。如果没找到,走第四步。
第四步:没有找到对应key的实例变量,就调用- (void)setValue:forUndefinedKey:方法。
到了第四步,如果你的对象的类中没有实现- (void)setValue:forUndefinedKey:方法,那么程序崩溃。
通过KVC的赋值过程,可以发现,系统通过直接访问的key对应的实例变量,然后进行的赋值操作。这就是为什么我们明明readonly属性没有生成setter方法,但是也可以通过KVC进行属性赋值的原因。
那么问题来了,我们既然添加属性的关键字readonly就是不想人家给我修改这个属性的值。但是,你突然偷偷摸摸给我改了,这可不行。那么有什么办法阻止吗?
当然,既然KVC通过的第二步直接查找属性关键字访问的实例变量赋值,那么我们拦截掉不就可以了吗?而且我们现在知道是否可以直接访问key对应的实例变量是通过一个+(BOOL)accessInstanceVariablesDirectly类方法返回值决定的。那么,好,我现在在我的对象类中重写这个方法,并且将方法的返回值返回NO。这样,如果没有setkey对应的方法,就不能直接访问我的实例变量了。
然后我们运行代码,程序通过kvc给readonly的key进行赋值,程序直接崩溃。崩溃信息,
*** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[<Person 0x60000175acc0> setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key address.'
OK,现在不能赋值了。但是程序崩溃也不是我们想要的。这时候我们在类中实现- (void)setValue:forUndefinedKey:方法(ps:这个方法我没做任何处理)。
这时候我们运行程序,程序不崩溃,并且打印对应key的value,发现也没有修改成功。
好,总结一下:
1、通过kvc的setValue:forKey方法可以给readonly属性赋值。
2、禁止kvc给readonly属性赋值。需要重写类方法+(BOOL)accessInstanceVariablesDirectly返回NO。并且在类中实现- (void)setValue:forUndefinedKey:方法。