What are the ways to turn a readonly property into readwrite property in Objective-C? Keep in mind I have no access to the source.
Reason for this: I need to do this in unit test for mock purposes.
The readonly means simply that no setter method was synthesized, and therefore using the dot notation to set a value fails with a compiler error. The dot notation fails because the compiler stops you from calling a method (the setter) that does not exist.
With PropertyDescriptor , check IsReadOnly . With PropertyInfo , check CanWrite (and CanRead , for that matter). You may also want to check [ReadOnly(true)] in the case of PropertyInfo (but this is already handled with PropertyDescriptor ): ReadOnlyAttribute attrib = Attribute.
The goal of the @property directive is to configure how an object can be exposed. If you intend to use a variable inside the class and do not need to expose it to outside classes, then you do not need to define a property for it. Properties are basically the accessor methods.
Read-only means that we can access the value of a property but we can't assign any value to it.
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. 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.
What you can do is use KVC, however. setValue:forKey:
will bypass setters and go straight to the ivar if it can find one. 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.
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:
//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 (e.g., @synthesize vanadium = erythronium;
), you'll need to know the ivar's name in order to use KVC.
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.
What you may think of doing is to guess the name of the ivar and add a setter at runtime.
Suppose your property is called foo
and that is has the copy
property.
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];
}
At this point you have added the setFoo:
method to your class at runtime, therefore you can access it by doing
YourClass yourObject = ...;
[yourObject setFoo:whatever];
I think overriding property that means redeclaring it again will work
In your .h file:
@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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With