繁体   English   中英

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

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

在Objective-C中将readonly属性转换为readwrite属性的方法有哪些? 请记住,我无法访问源代码。

原因:我需要在单元测试中进行此操作以进行模拟。

如果没有访问类的主要实现块,或者至少包含它的编译单元,则无法更改此属性的功能,因为您无法访问该单元外部的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。

我认为重写财产意味着再次重新宣布它将起作用

在你的.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]

您不能简单地将属性转换为readwrite并希望访问setter,因为setter本身尚未合成,因此根本不存在。

你可能想到的是猜测ivar的名称并在运行时添加一个setter。

假设您的属性名为foo ,并且具有copy属性。

  1. 猜猜伊娃的名字。 让我们试试_foo

  2. 准备一个二传手

     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. 将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]; } 

此时,您已在运行时将setFoo:方法添加到类中,因此您可以通过执行操作来访问它

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

暂无
暂无

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

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