在Objective-C中,如何将只读属性转换为可读写属性?请注意,我无法访问源代码。
原因:我需要在单元测试中模拟这种情况。
原因:我需要在单元测试中模拟这种情况。
如果你没有访问类的主要实现块,或者至少包含它的编译单元,就不能像这样改变属性的功能,因为你无法访问该单元外的 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
的ivar),这将失败--实际上会引发异常(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。
您不能简单地将属性设为readwrite
,希望访问setter,因为setter本身尚未被合成,因此根本不存在。
您可以考虑的做法是猜测ivar的名称,并在运行时添加setter。
假设您的属性名为foo
,并且具有copy
属性。
Guess that the name of the ivar. Let's try with _foo
.
Prepare a setter
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]);
}
Add the setter to the class in the resolveInstanceMethod:
class method
+ (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];
resolveInstanceMethod:
可能是调用 class_addMethod()
的更好选择位置。这基本上就是它记录的目的。 - jscs我认为覆盖属性即重新声明它将起作用
在您的.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]