test for read-only property vs. set/get key -- obj-c / cocoa - objective-c

If all i have is a list of keys, is there an elegant way to test an object for read-only vs. read/write properties? I realize I could string-bang the key:
NSString *setterString = [#"set" stringByAppendingString:[someKeyString capitalizedString]];
BOOL isReadWrite = [myObject respondsToSelector:NSSelectorFromString(setterString)];
Or better yet try to set a value for the key and check for an NSUndefinedKeyException - but using exceptions for non-exceptional behavior seems like poor form.
To be clear, i'd like to programmatically audit an object's properties and tell the difference between, for instance,
#property (readonly) NSString *someReadOnlyKey
#property NSString *someReadWriteProperty
edit: To be more clear, it shouldn't matter if the keys are implemented as #propertys or manual getter/setter. Only concerned about public interface. And thanks for asking what I'm trying to accomplish - that's probably more important to get right in the first place.
I'm trying to sketch out some code that generates graphic representations of an object's keys. All the keys are known beforehand - but I won't always know which keys will be sett-able (it's up to the particular subclass to implement)

You have two valid approaches:
the respondsToSelector: method
the runtime "trick" as exposed in Tommy's answer
I'll try to summarize the implication of both approaches, along with their drawbacks. You can then choose the approach more suitable to your needs.
Case 1
//MyClass.h
#property (readonly) NSString *someReadOnlyKey;
runtime => readonly
respondsToSelector => NO
Ok, everything works as expected.
Case 2
//MyClass.h
#property (readonly) NSString *someReadOnlyKey;
///////////////////////////////////////////////////////////////////////////
//MyClass.m
#property (readwrite) NSString *someReadOnlyKey;
runtime => readwrite
respondsToSelector => YES
If the property definition has been overridden, you'll get the actual property attribute being used. It doesn't matter whether you query about it from where the setter is visible, i.e. inside the class definition, or from outside. You will get the actual definition that has been used to synthesize the accessor methods.
Case 3
//MyClass.h
#property (setter=myCoolSetter) NSString *someReadOnlyKey;
runtime => readwrite
respondsToSelector => NO
With a custom setter name, the respondsToSelector trick won't work anymore, whereas the runtime is still providing the correct information.
Case 4
//MyClass.h
#property (readonly) NSString *someReadOnlyKey;
///////////////////////////////////////////////////////////////////////////
//MyClass.m
- (void)setSomeReadOnlyKey:(NSString *)someReadOnlyKey {
_someReadOnlyKey = someReadOnlyKey;
}
runtime => readonly
respondsToSelector => YES
This time the runtime is failing, since the setter is there, but the property definition "doesn't know" about it.

Assuming you're fine with asking the metaclass (ie, you don't want to allow for potential instance-specific patches to the dispatch table), you can get property attributes from the runtime. Specifically:
// get the property; yes: that's a C string. This can see only things
// declared as #property
objc_property_t property =
class_getProperty([instance class], "propertyName");
/* check property for NULL here */
// get the property attributes.
const char *propertyAttributes = property_getAttributes(property);
You can then check those attributes for read-only:
NSArray *attributes =
[[NSString stringWithUTF8String:propertyAttributes]
componentsSeparatedByString:#","];
return [attributes containsObject:#"R"];
If you want to be completely thorough, you should also check protocols via class_copyProtocolList and protocol_getProperty, in order to catch any #propertys that are incorporated into the class that way and — as noted below by Gabriele — some caveats apply around class extensions.

This might be one of the few times I might consider the exception route - that is of course if you need handle KVC.
The reason being that just checking for a set<key>: style method name is not really enough to determine if something is really settable, especially when you bring KVC into it.
KVC has well defined search patterns for when you try to set values, which can be seen in the Key Value Coding Programming Guide. This search path has many stops and even allows objects a last chance "are you sure you don't want to handle this setter" method with setValue:forUndefinedKey:, which can be used to prevent the NSUndefinedKeyException exception being thrown

You can use the following:
if ([obj respondsToSelector:#selector(setSomeReadOnlyKey:someReadOnlyKey:)]) {
// Not a readonly property
}
else
// Readonly property
This should work. Essentially, I am checking if the setter method exists.

Related

Why do I have to cast the delegate?

I have a class and this class has a delegate protocol. I create an object in the main class using this class and assigned the main class as the delegate. The main class has a property I would like to read inside the created class. Then I do this:
BOOL prop = [self.delegate myProperty];
Xcode complains that "delegate may not respond to myProperty"
If I am sending a message to self.delegate to read myProperty, and generally Xcode never complains when you send a message to an not casted object, why do I have to do that
BOOL prop = [(myMainClass *)self.delegate myProperty];
to make it work?
To be more clear, here is an example of a message sent to an object without having to cast:
[self.myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
int Number = [[obj dictionaryOfNames] count];
}];
self.myArray is an array of objects from another class that are dictionaries and have this property dictionaryOfNames. Xcode never asked me to cast this:
int Number = [[(myOtherClass *)obj dictionaryOfNames] count];
as I had to above for myProperty.
Different classes can conform to a protocol. If you declare that you conform to a protocol you just say that you will implement the requiered methods but you can implement it in a UIView, UIViewController, MyOwnClass, MyOtherClass etc.
Thats why a property is normally declared like this
#property (nonatomic, weak) id <MyProtocol> delegate;
So you just say your delegate is an object which conform to the protocol.
You haven't shown enough code to give a completely definitive answer, but in general terms I would expect that the definition of your delegate is not just id, you've probably used NSObject* or something similar.
The compiler is doing "static" analysis of your source code and trying to determine whether or not the object specified by "self.delegate" might implement that method. If the data type is, say, NSObject*, then the compiler looks through that specific class definition to see if your method is present; if it isn't, then you'll get a warning.
If the data type of the message receiver is id, the compiler tends to give up and say "well, it could be anything so I'll assume this will work".
The result of the expression [obj dictionaryOfNames] is probably of type NSDictionary and the compiler can see that that particular class does respond to the count method.
Note, you can also get this problem if you have specified a class name for a property, but the compiler cannot see the entire class definition from this file. For example, if you have
myobject.h:
#class Something;
#interface MyObject
#property (retain) Something *delegate;
#end
myobject.m:
#import "myobject.h"
[self.delegate doItYouFool];
then the compiler can see that the result of the expression 'self.delegate' is of type Something* but it can not see the actual definition of that class and thus can't look through its supported messages. This usually results in a warning about 'forward definitions'. To fix it, you should import "something.h" into the .m file, so that the compiler has full knowledge about the classes it is working with. To just silence the warning, you cast to id
[(id)self.delegate doItYouFool];
You may, of course, also be getting warnings that 'doItYouFool' isn't a known method, again because you haven't included the header file that defines that message.

Are getters the same for all properties in objective-c?

This simple question is bugging me. Are getters the same for
#property (nonatomic, retain) NSString *name
#property (nonatomic, copy) NSString *name
- (NSString*) name{
return name;
}
According to the documentation
A property declaration, however, provides additional information about
how the accessor methods are implemented (as described in “Property
Declaration Attributes”).
Both the getter and the setter behavior are defined by property declarations. In your example that is correct since it is defined nonatomic but if nonatomic were missing from the declaration it would would be implemented similar to this
- (NSString*) name{
[_internal lock]; // lock using an object-level lock
id result = [[name retain] autorelease];
[_internal unlock];
return result;
}
This of course is only true if you use #synthesize and do not override or change the getter and setter methods.
Yes, as the other answers state, the getters are the same. The options retain, copy, and assign determine how to generate setters, but not the names even of those.
If you want to use a different getter name, for instance if you have a BOOL such as the UIApplication property idleTimerDisabled, then you do this by specifically assigned the getter name:
#property(nonatomic, getter=isIdleTimerDisabled) BOOL idleTimerDisabled
Without an override such as this, the getter name is always the property name.
Yes, they are the same. retain, copy, and assign only give the compiler instructions on how to generate setters, not getters.
Yes, copy and retain only affect the setter and not the getter. On a side note, you should chose copy instead of retain for immutable objects like NSString.
If you're using ARC (Automated Reference Counting), then the getters should all look like what you have above. However, the new standard with ARC is to use strong and weak instead of retain and assign, respectively. retain and assign will still work and are synonymous with the strong and weak, but any newly generated files will use those words instead, so it's important to understand what they mean.
If you aren't using ARC (still an option, even in iOS 5 and Lion) getters can be different depending on retain, copy, or assign.
retain and copy both look like:
- (NSString *)name {
return [[name retain] autorelease];
}
assign is pretty basic, but is usually used more for weak references (like delegates):
- (id)delegate {
return delegate;
}
I went into some detail on this because it's important to understand when using ARC, the code for all 3 look the same, but the behavior is still very much like what's written above. If you're using atomic instead of nonatomic then you need to add some locking/unlocking lines to make the method thread-safe. In any case, it's generally better to use the default accessors generated with #synthesize unless you want to do something really tricky.
Yes, they are. The getter name dependce only of properties name.

"getter" keyword in #property declaration in Objective-C?

I noticed some code example in Apple's documentation shows the following style when declaring the property:
#property (nonatomic, getter=isActivated) BOOL activated;
I understand it allows you to specify a certain name for your getter method. I'd like to know what is the reason and advantage to use this style.
Will I be able to use the dot notation to get the value (e.g. BOOL aBool = someObject.isActivated)? Or should I use
[someObject isActivated];
to access the property? Thanks!
No, the getter keyword only changes the method name. The idea is that you'll access the property just like a variable:
if (self.activated) { ... }
self.activated = YES;
But when you're sending a message to the object, it's readable code: if ([self isActivated]) { ... }.
Kind of the latter. You don’t have to use the method—calling someObject.activated will still work—but it lets you improve the semantics of your class’s interface. A method called -activated could return the value of the ivar activated, or it could do something more esoteric (like activating the object); isActivated clearly returns a Boolean value for whether or not the object is “activated”.

#property question

In my implementation I have getters and setters like below. I want to use properties and synthesize the getters and setters but have a few questions.
- (NSString *)title {
return title;
}
- (void)setTitle:(NSString *)value {
if(title != value) {
[title release];
title = [value retain];
}
}
If I was to convert that to a property, what attributes would I use? Am I right in thinking:
readwrite so both getters and setters are present
retain so that it increase the retain value of the value string so the object don't lose it.
Am I right with the above?
One final thing. I have the method below ...
- (void)setReleaseDate:(NSString *)value {
// YYYY-MM-DD HH:MM:SS +HHMM
if([releaseDate description] != value) {
[releaseDate release];
releaseDate = [[NSDate alloc] initWithString:value];
}
}
Am I right in thinking I still have to include that method because it contains code that the synthesized getter would not include?
Thanks.
For your title property, you can declare it in your class interface as follows:
#property (nonatomic, retain) NSString* title;
Which is the same as the following:
#property (readwrite, nonatomic) NSString* title;
readwrite is a default setting. Most of the time you will want setters for your properties, so for the times when you don't you would use the non-default readonly to specify this.
The nonatomic part basically means that the accessors will be faster, and is typically used. You can find out more information about this here: What does the property "Nonatomic" mean?.
For your second question, you can implement your own accessors if you wish. If you do this then it kind of 'overrides' the accessor that would be generated by Objective-C. Remember that you have to keep to the naming conventions. So in your example, the "setReleaseDate:" method you've defined would be used for the setter method for the property "releaseDate" - which is completely correct! :) The problem you have though is that you're passing an *NSString** to set the date, which means that this method won't override the default setter that would be used if you synthesized the property. You have to pass a value of the same type as the one you're setting as the single argument, so for this case you would have to pass an *NSDate**.
You must also ensure that if you provide your own implementation of an accessor that it does what is declared in the interface. I presume your releaseDate property should be declared as retain.
Your assertion about using readwrite as well as retain is correct as it would create semantically equivalent code to what you have posted.
The releasedate property setter can't be synthesized as you're transforming a NSString into a NSDate to store it, that also avoids common issues with NSString properties, for which you'd better use copy to avoid problems with NSMutableString.
Other than that, your code is fine, except that for string comparison you may want to replace the simple pointer check != with isEqualToString, see Comparing Strings in Cocoa.
It is common, though not always required, to use copy semantics for NSString properties, to avoid issues with NSMutableString objects being changed behind your back.
Otherwise, you seem to be pretty much on top of it.

Custom property attributes in Objective-c

Can custom property attributes be created in Objective-C just like in VB.NET? For example, in VB.NET you can create the "Browsable" attribute and read it at runtime to determine whether you should display a property or not.
Public Class Employee
<Browsable(True)> _
Public Property Property1() As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
<Browsable(False)> _
Public Property Property2() As String
Get
End Get
Set(ByVal Value As String)
End Set
End Property
End Class
I would like to do the same in Objective-C, even if it is a fixed attribute that can only be set at compile time and cannot be changed at all.
What I'm trying to do is to add an attribute to properties of my class to determine whether the properties should be serialized or not.
I know the standard Objective-C attributes (readonly, nonatomic, etc.), but those don't help me... unless you have a creative way of using them. I also looked into using C attributes with the __attribute__(("Insert attribute here")) keyword, but C has specific attributes that serve specific purposes, and I'm not even sure you can read them at runtime. If I missed one that can help me, let me know.
I tried using typdef. For example:
typdef int serializableInt;
serializableInt myInt;
and use the property_getAttributes() Objective-C runtime function, but all it tells me is that myInt is an int. I guess typedef is pretty much like a macro in this case... unless I can create a variable of type serializableInt at runtime. Anyhow, here's Apple's documentation on the values you get from property_getAttributes().
The other requirement is that this attribute has to work with NSObject sub-classes as well as primitive data types. I thought about the idea of adding to the class a black lists or white lists as an ivar that would tell me which properties to skip or serialize, which is basically the same idea. I'm just trying to move that black/white list to attributes so it's easy to understand when you see the header file of a class, it's consistent across any class I create and it's less error prone.
Also, this is something to consider. I don't really need the attribue to have a value (TRUE or FALSE; 1, 2, 3; or whatever) because the attribute itself is the value. If the attribute exists, then serialize; otherwise, skip.
Any help is appreciated. If you know for sure that this is not possible on Objective-C, then let me know. Thanks.
If you want to add attribute to property, class, method or ivar, you can try to use github.com/libObjCAttr. It's really easy to use, add it via cocoapods, and then you can add attribute like that:
#interface Foo
RF_ATTRIBUTE(YourAttributeClass, property1 = value1)
#property id bar;
#end
And in the code:
YourAttributeClass *attribute = [NSDate RF_attributeForProperty:#"bar" withAttributeType:[YourAttributeClass class]];
// Do whatever you want with attribute, nil if no attribute with specified class
NSLog(#"%#", attribute.property1)
unless i've missed your point…
i'd recommend declaring a protocol. then using instances of objc objects as variables in your objc classes which adopt the protocol.
#interface MONProtocol
- (BOOL)isSerializable;
- (BOOL)isBrowsable;
/* ... */
#end
#interface MONInteger : NSObject <MONProtocol>
{
int value;
}
- (id)initWithInt:(int)anInt;
#end
#interface MONIntegerWithDynamicProperties : NSObject <MONProtocol>
{
int value;
BOOL isSerializable;
BOOL isBrowsable;
}
- (id)initWithInt:(int)anInt isSerializable:(BOOL)isSerializable isBrowsable:(BOOL)isBrowsable;
#end
// finally, a usage
#interface MONObjectWithProperties : NSObject
{
MONInteger * ivarOne;
MONIntegerWithDynamicProperties * ivarTwo;
}
#end
if you want to share some implementation, then just subclass NSObject and extend the base class.
you'd then have a few variants to write for the types/structures you want to represent.
The deficiency with the other answers I've seen so far is that they are implemented as instance methods, i.e., you need to have an instance already before you can query this metadata. There are probably edge cases where that's appropriate, but metadata about classes should be implemented as class methods, just as Apple does, e.g.:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key { }
We could imagine our own along similar lines:
+ (BOOL)keyIsBrowsable:(NSString*)key { }
or
+ (NSArray*)serializableProperties { }
Let's imagine our class is called FOOBar, and we want to know whether the baz key is browsable. Without having to create a FOOBar we can just say:
if ([FOOBar keyIsBrowsable:#"baz"]} { ... }
You can do pretty much anything with this technique that can be done with custom attributes. (Except for things like the Serializable attribute which require cooperation from the compiler, IIRC.) The nice thing about custom attributes, though, is that it is easy to distinguish at a glance what is metadata and what is intrinsic to that class's actual functionality, but I think that's a minor gain.
(Of course, you may have to check for the existence of the keyIsBrowsable: selector, just as you'd have to check for the existence of a specific custom attribute. Again, custom attributes have a slight leg up here, since we can tell the .NET runtime to give them all to us.)
I've come across a similar issue whe serializing objects. My solution is to add a #property (nonatomic, readonly) NSArray *serialProperties; which has a custom getter that returns the names (as NSString*) of the properties of this (sub-)class that should be serialized.
For example:
- (NSArray *)serialProperties {
return #[#"id", #"lastModified", #"version", #"uid"];
}
Or in a subclass:
- (NSArray *)serialProperties {
NSMutableArray *sp = [super serialProperties].mutableCopy;
[sp addObject:#"visibleName"];
return sp;
}
You can then easily get all properties and their values via [self dictionaryWithValuesForKeys:self.serialProperties].
You can't add custom properties other than what sdk has provided..
.
But there is a work around to attain your objective...
#interface classTest:NSObject
#property(strong,nonatomic)NSString *firstName;
#property(strong,nonatomic)NSString *lastName;
#property(strong,nonatomic)NSMutableDictionary *metaData;
#end
#implementation classTest
- (id) init
{
self = [super init];
//Add meta data
metaData=[[NSmutableDictionary alloc]init];
//
if( !self ) return nil;
return self;
}
#end
so use the dictionary to add and retrieve meta data...
i hope it helps....