20

I want to create a method like this:

- (void) checkAndUpdateStringProperty: (NSString **) property{
   if (...) 
      *property = @"whatever";
}

I want to use this to pass in a property for the same class:

- (void) somewhereElse{
       ....
       [self checkAndUpdateStringProperty: &self.productName];
}

But this gives a syntax error, saying "Address of property expression requested". Any reason why? If I replace the class property with a local variable, it works fine:

- (void) somewhereElse{
       NSString *name = nil;
       [self checkAndUpdateStringProperty: &name];
}
Lebyrt
  • 1,398
  • 1
  • 9
  • 18
Z S
  • 6,465
  • 11
  • 47
  • 89

5 Answers5

27

Properties should only ever be accessed through their getter and setter methods, with the exception of initializing and deinitializing them in the object's init and dealloc methods (when the object is not in a fully consistent state) -- accessing them any other way defeats their purpose and does not respect their attributes (retain, copy, etc.).

The reason why it doesn't compile is because property accessors are syntactic sugar for method calls, and you can't take the address of the return value of a method. self.productName is identical to [self productName], and &[self productName] makes no sense.

If you really want to do this, don't do it with a property, do it with just a regular variable (such as a local variable or ivar), and document the semantics of your function: does the calling function need to release the returned object? Etc. For example, the various Apple methods which take an NSError** specify that the error object which is returned, if any, is an autoreleased object, so you should not release it yourself unless you also retain it.

Adam Rosenfield
  • 360,316
  • 93
  • 484
  • 571
  • 1
    The exception to accessing property ivars directly is in `init` and `dealloc` where the class is not in a fully complete form and any side effects could be a problem. – zaph Sep 30 '11 at 21:52
  • 2
    Thanks. Actually figured out that (since this was a NSManagedObject) I could pass in the property's name and access the value using valueForKey, and set it inside the 'check' method using setValue:forKey: Didn't need to do the double-pointer indirection. – Z S Oct 01 '11 at 00:09
7

There are very few places in the Apple APIs that a value is passed in this fashion, the main one being NSError. It is really best to just return the result. If you do have a reference parameter the general Apple rule is that it should be the last parameter that that the method name begin with "get" as in getCheckAndUpdateStringProperty.

But you can do it, it is just not what we are used to.

The error you are seeing is that self.productName is really shorthand for the method call: [self productName] so &self.productName is interpreted as &[self productName].

Further in this case if productName is a @property with a retain attribute the reference counting will be subverted.

zaph
  • 108,117
  • 19
  • 176
  • 215
5

If you are really careful about the ownership attributes, you can get around this issue by staring at the following:

[self property]

vs:

self.property

vs:

self->property

Once you are clear about the difference you can try this:

[self checkAndUpdateStringProperty: &self->productName]

Note the use of: ->

I use this all the time when the property attribute is assign and when it is a primitive, non object type: (float, int, ...)

verec
  • 4,634
  • 3
  • 31
  • 39
0

create a property of that variable,which you want to access in another class (@property(strong,nonatomic) ;) and then synthesise it.... (@synthesize ;) and then access via object name of that class,where you define property and synthesise it.....

0

I'd do this using the selectors instead (you'll have to convert it to a c function to get it to work without warning (see here):

- (void) checkAndUpdateStringProperty: (SEL)setter {
   if (...) 
     IMP imp = [self methodForSelector:setter];
     void (*func)(id, SEL, id) = (void *)imp;
     func(self, setter, @"whatever");
}

Use this to pass in the selector for the same class:

- (void) somewhereElse{
       ....
       [self checkAndUpdateStringProperty:@selector(setProductName:)];
}
Community
  • 1
  • 1
dulgan
  • 6,563
  • 3
  • 38
  • 45