简体   繁体   English

如何在Objective-C中将readonly属性转换为readwrite属性?

[英]How to turn a readonly property into readwrite property in Objective-C?

What are the ways to turn a readonly property into readwrite property in Objective-C? 在Objective-C中将readonly属性转换为readwrite属性的方法有哪些? Keep in mind I have no access to the source. 请记住,我无法访问源代码。

Reason for this: I need to do this in unit test for mock purposes. 原因:我需要在单元测试中进行此操作以进行模拟。

You can't change the property's functionality like this without access to the main implementation block of the class, or at least the compilation unit that contains it, because you don't have any access to the ivar outside that unit. 如果没有访问类的主要实现块,或者至少包含它的编译单元,则无法更改此属性的功能,因为您无法访问该单元外部的ivar Even if you were to add a setter to the class in a category, you wouldn't be able to affect the class's storage any more than you can from entirely outside the class. 即使你要在一个类别中为类添加一个setter,你也不可能完全影响类的存储,而不是完全在类之外。

What you can do is use KVC, however. 但是,您可以使用KVC。 setValue:forKey: will bypass setters and go straight to the ivar if it can find one . setValue:forKey: 如果可以找到一个,将绕过setter并直接进入ivar。 You can use this to set any value you like even for a property that was declared readonly , provided there's backing storage whose name you know. 您可以使用它来设置您喜欢的任何值,即使是已经声明为readonly的属性,前提是您知道其名称的后备存储。

It goes like this: 它是这样的:

//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;
}

Like I said, this will fail -- actually raise an exception -- if there's neither a setter nor an ivar by the same name (or with an underscore appended _vanadium (KVC is pretty smart), such as if the property's value is entirely calculated: 就像我说的那样,这会失败 - 实际上会引发一个异常 - 如果既没有一个setter也没有一个同名的ivar(或者附加一个下划线_vanadium (KVC非常聪明),比如该属性的值是完全计算的:

//Passaquisset.m

#import "Passaquisset.h"

@implementation Passaquisset

/** KVC will fail with this version! **/

- (NSString *)vanadium
{
    return @"Number 23";
}

@end

For completeness, let me mention that if the property is backed by an ivar of an entirely different name (eg, @synthesize vanadium = erythronium; ), you'll need to know the ivar's name in order to use KVC. 为了完整起见,让我提一下,如果该属性由一个完全不同名称的ivar支持(例如@synthesize vanadium = erythronium; ),您需要知道ivar的名称才能使用KVC。

I think overriding property that means redeclaring it again will work 我认为重写财产意味着再次重新宣布它将起作用

In your .h file: 在你的.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

or 要么

i am not tested but i think ,you have to try following 我没有经过测试,但我认为,你必须尝试以下

[youReadOnlyrProperty retain]

You cannot simply turn the property into readwrite and hope to access the setter, since the setter itself has not been synthesized, therefore it doesn't exist at all. 您不能简单地将属性转换为readwrite并希望访问setter,因为setter本身尚未合成,因此根本不存在。

What you may think of doing is to guess the name of the ivar and add a setter at runtime. 你可能想到的是猜测ivar的名称并在运行时添加一个setter。

Suppose your property is called foo and that is has the copy property. 假设您的属性名为foo ,并且具有copy属性。

  1. Guess that the name of the ivar. 猜猜伊娃的名字。 Let's try with _foo . 让我们试试_foo

  2. 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]); } 
  3. Add the setter to the class in the resolveInstanceMethod: class method 将setter添加到resolveInstanceMethod: class方法中的类

     + (BOOL) resolveInstanceMethod:(SEL)aSEL { if (aSEL == @selector(setFoo:)) { class_addMethod(self, @selector(setFoo:), (IMP)fooSetter, "v@:@"); return YES; } return [super resolveInstanceMethod:aSel]; } 

At this point you have added the setFoo: method to your class at runtime, therefore you can access it by doing 此时,您已在运行时将setFoo:方法添加到类中,因此您可以通过执行操作来访问它

YourClass yourObject = ...;
[yourObject setFoo:whatever];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM