Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

objective-c

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.

like image 722
Boon Avatar asked Jan 27 '13 17:01

Boon


People also ask

What is ReadOnly in Objective C?

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.

How do you know if a property is ReadOnly?

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.

What is Property OBJC?

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.

What is read only property in IOS?

Read-only means that we can access the value of a property but we can't assign any value to it.


3 Answers

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.

like image 64
jscs Avatar answered Nov 13 '22 08:11

jscs


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.

  1. Guess that the name of the ivar. Let's try with _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

    + (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];
like image 24
Gabriele Petronella Avatar answered Nov 13 '22 07:11

Gabriele Petronella


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]
like image 24
Ravindra Bagale Avatar answered Nov 13 '22 08:11

Ravindra Bagale