0

Currenlty I have an issue with ocmock for unit testing because some @dynamic properties have no getters or setters when being mocked. I'm using class_addmethod to add the getters and setters for all @dynamic properties. My issue is this

void accessorSetter(id self, SEL _cmd, id newValue)
{
    NSString *method = NSStringFromSelector(_cmd);
    id value = [newValue copy];
// remove set prefix from string
    NSString *anID = [[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""];
    anID = [anID stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[anID substringWithRange:NSMakeRange(0, 1)] lowercaseString]];
    [self setValue:value forKey:anID];
}

causes an infite loops since setValue calls the setter. I think I can use c++ syntax like self->somevar = value to avoid the infinite loop. My question is how do I do this assigment when the name of the variable is a string? anID is the name of the variable and i can't do self->anID = aValue cuz anID is not a property. How do I convert it to a variable name? Or how can I set the property without creating the infinite loop?

Alex Reynolds
  • 6,264
  • 4
  • 26
  • 42

1 Answers1

0

You can use object_setInstanceVariable or object_setIvar and object_getInstanceVariable. You can take a look at the Objective C Runtime Reference for more information.

EDIT: If you are using ARC, you can't use object_setInstanceVariable. You'll be stuck with object_setIvar.

EDIT 2: See this answer if you are using ARC.

Example usage:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Test : NSObject
@property (nonatomic, strong) NSString *test;
@end
@implementation Test
- (id)init {
    if (self = [super init]) {
    self.test = @"asdasd";
    }
    return self;
}
- (void)setTest:(NSString*)test {
    Ivar var_desc = object_getInstanceVariable(self, [@"_test" cStringUsingEncoding:NSUTF8StringEncoding], NULL);
    NSLog(@"Setting _test to %@", test);
    object_setIvar(self, var_desc, test);
}
@end

int main(int argc, char *argv[])
{
    Test *tst = [[Test alloc] init];
    NSLog(@"prop: %@", tst.test);
    return 0;
}

Output:

2013-12-11 18:19:32.038 ivar_tst[97090:507] Setting _test to asdasd
2013-12-11 18:19:32.040 ivar_tst[97090:507] prop: asdasd
Community
  • 1
  • 1
Ivan Genchev
  • 2,746
  • 14
  • 25
  • Great idea. Still seeming to cause an infinite loop. I got the ARC methods but I can see using breakpoints that is calling itself over and over. Not sure if there's a way around this but it seems like you can't override a setter method at runtime. – Alex Reynolds Dec 11 '13 at 18:02
  • Your problem is definitely somewhere else... see my edited answer with an example, which is overriding the setter and using the `object_setIvar` without causing infinite loops. – Ivan Genchev Dec 11 '13 at 18:22
  • You can also see this answer (http://stackoverflow.com/questions/7819092/how-can-i-add-properties-to-an-object-at-runtime) as an example of adding properties at runtime, which is also using `object_getInstanceVariable` and `object_setIvar`. – Ivan Genchev Dec 11 '13 at 18:31
  • Does your example work with `@dynamic` properties? I think my issue is related to using `@dynamic`. – Alex Reynolds Dec 11 '13 at 18:58
  • Ya just saw that I was doing `class_getInstanceVariable(self, [anID cStringUsingEncoding:NSUTF8StringEncoding]);` which broke but `class_getInstanceVariable(object_getClass(self), [anID cStringUsingEncoding:NSUTF8StringEncoding]);` worked better – Alex Reynolds Dec 11 '13 at 19:05
  • See the answer for the question I added in my previous comment, there's a very nice example there. – Ivan Genchev Dec 11 '13 at 19:08
  • Setter seems to be working. Xcode is crashing on ever po statement but lets be honest xcode 5 isn't that stable anyways. Thanks ivan – Alex Reynolds Dec 11 '13 at 20:00