Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Readonly Properties in Objective-C?

I have declared a readonly property in my interface as such:

 @property (readonly, nonatomic, copy) NSString* eventDomain; 

Maybe I'm misunderstanding properties, but I thought that when you declare it as readonly, you can use the generated setter inside of the implementation (.m) file, but external entities cannot change the value. This SO question says that's what should happen. That is the behavior I'm after. However, when attempting to use the standard setter or dot syntax to set eventDomain inside of my init method, it gives me an unrecognized selector sent to instance. error. Of course I'm @synthesizeing the property. Trying to use it like this:

 // inside one of my init methods  [self setEventDomain:@"someString"]; // unrecognized selector sent to instance error 

So am I misunderstanding the readonly declaration on a property? Or is something else going on?

like image 733
Alex Avatar asked Jan 03 '11 17:01

Alex


People also ask

How do you set a read-only property?

The error "Cannot assign to 'X' because it is a read-only property" occurs when we try to change the value of a read-only property in a class or an object. To solve the error, remove the readonly modifier or use a type assertion to change the value of the property.

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.

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 copy property?

"The copy attribute is an alternative to strong. Instead of taking ownership of the existing object, it creates a copy of whatever you assign to the property, then takes ownership of that. Only objects that conform to the NSCopying protocol can use this attribute..."


2 Answers

You need to tell the compiler that you also want a setter. A common way is to put it in a class extension in the .m file:

@interface YourClass ()  @property (nonatomic, copy) NSString* eventDomain;  @end 
like image 88
Eiko Avatar answered Sep 17 '22 13:09

Eiko


Eiko and others gave correct answers.

Here's a simpler way: Directly access the private member variable.

Example

In the header .h file:

@property (strong, nonatomic, readonly) NSString* foo;

In the implementation .m file:

// inside one of my init methods self->_foo = @"someString"; // Notice the underscore prefix of var name. 

That’s it, that’s all you need. No muss, no fuss.

Details

As of Xcode 4.4 and LLVM Compiler 4.0 (New Features in Xcode 4.4), you need not mess with the chores discussed in the other answers:

  • The synthesize keyword
  • Declaring a variable
  • Re-declaring the property in the implementation .m file.

After declaring a property foo, you can assume Xcode has added a private member variable named with a prefix of underscore: _foo.

If the property was declared readwrite, Xcode generates a getter method named foo and a setter named setFoo. These methods are implicitly called when you use the dot notation (my Object.myMethod). If the property was declared readonly, no setter is generated. That means the backing variable, named with the underscore, is not itself readonly. 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.

The simplest way around this is to directly access the member variable, named with the underscore. You can do so even without declaring that underscore-named variable! Xcode is inserting that declaration as part of the build/compile process, so your compiled code will indeed have the variable declaration. But you never see that declaration in your original source code file. Not magic, just syntactic sugar.

Using self-> is a way to access a member variable of the object/instance. You may be able to omit that, and just use the var name. But I prefer using the self+arrow because it makes my code self-documenting. When you see the self->_foo you know without ambiguity that _foo is a member variable on this instance.


By the way, discussion of pros and cons of property accessors versus direct ivar access is exactly the kind of thoughtful treatment you'll read in Dr. Matt Neuberg's Programming iOS book. I found it very helpful to read and re-read.

like image 40
Basil Bourque Avatar answered Sep 20 '22 13:09

Basil Bourque