在Objective-C中将readonly属性转换为readwrite属性有哪些方法?请记住,我无法访问来源。
原因:我需要在单元测试中进行此操作以进行模拟。
答案 0 :(得分:8)
如果没有访问类的主要实现块,或者至少包含它的编译单元,您无法更改此属性的功能,因为您无权访问 ivar 在那个单位之外。即使你要在一个类别中为类添加一个setter,你也不可能完全影响该类的存储,而不是完全在课外。
但是,您可以使用KVC。如果可以找到一个,setValue:forKey:
会绕过setter并直接进入ivar 。您可以使用它来设置您喜欢的任何值,即使是已声明为readonly
的属性,前提是您知道名称的后备存储。
它是这样的:
//Passaquisset.h
#import <Foundation/Foundation.h>
@interface Passaquisset : NSObject
@property (copy, readonly, nonatomic) NSString * vanadium;
@end
//Passaquisset.m
#import "Passaquisset.h"
@implementation Passaquisset
@synthesize vanadium;
- (id) init {
self = [super init];
if( !self ) return nil;
vanadium = @"Number 23";
return self;
}
@end
//Elsewhere...
#import <Foundation/Foundation.h>
#import "Passaquisset.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Passaquisset * pq = [Passaquisset new];
NSLog(@"%@", [pq vanadium]);
[pq setValue:@"Number 24" forKey:@"vanadium"];
NSLog(@"%@", [pq vanadium]);
}
return 0;
}
就像我说的那样,这会失败 - 实际上会引发异常 - 如果既没有setter也没有同名的ivar(或者附加了下划线_vanadium
(KVC非常聪明),例如如果财产的价值完全计算:
//Passaquisset.m
#import "Passaquisset.h"
@implementation Passaquisset
/** KVC will fail with this version! **/
- (NSString *)vanadium
{
return @"Number 23";
}
@end
为了完整起见,请允许我提一下,如果该属性由名称完全不同的ivar(例如@synthesize vanadium = erythronium;
)支持,则需要知道 ivar的名称。为了使用KVC。
答案 1 :(得分:2)
我认为重写属性意味着再次重新声明它将起作用
在你的.h文件中:
@property (readonly, copy) NSString *yourProperty;
In your .m file:
@interface MyClass ()
// Redeclare property as readwrite
@property (readwrite, copy) NSString *yourProperty;
@end
@implementation MyClass
@synthesize yourProperty;
@end
或
我没有经过测试,但我想,你必须尝试以下
[youReadOnlyrProperty retain]
答案 2 :(得分:2)
您不能简单地将属性转换为readwrite
并希望访问setter,因为setter本身尚未合成,因此根本不存在。
您可能想到的是猜测ivar的名称并在运行时添加一个setter。
假设您的属性名为foo
且属性为copy
。
猜猜伊娃的名字。让我们试试_foo
。
准备一个二传手
void fooSetter(id self, SEL _cmd, id newFoo) {
Ivar ivar = class_getInstanceVariable(self, "_foo");
id oldFoo = object_getIvar(self, ivar);
if (oldFoo != newFoo)
object_setIvar(self, ivar, [newFoo copy]);
}
将setter添加到resolveInstanceMethod:
类方法
+ (BOOL) resolveInstanceMethod:(SEL)aSEL {
if (aSEL == @selector(setFoo:)) {
class_addMethod(self, @selector(setFoo:), (IMP)fooSetter, "v@:@");
return YES;
}
return [super resolveInstanceMethod:aSel];
}
此时您已在运行时将setFoo:
方法添加到您的类中,因此您可以通过执行
YourClass yourObject = ...;
[yourObject setFoo:whatever];