Is it possible to add properties to an Objective C object at runtime?
It’s possible to add formal properties to a class via class_addProperty():
BOOL class_addProperty(Class cls,
const char *name,
const objc_property_attribute_t *attributes,
unsigned int attributeCount)
The first two parameters are self-explanatory. The third parameter is an array of property attributes, and each property attribute is a name-value pair which follow Objective-C type encodings for declared properties. Note that the documentation still mentions the comma-separated string for the encoding of property attributes. Each segment in the comma-separated string is represented by one objc_property_attribute_t instance. Furthermore, objc_property_attribute_t accepts class names besides the generic # type encoding of id.
Here’s a first draft of a program that dynamically adds a property called name to a class that already has an instance variable called _privateName:
#include <objc/runtime.h>
#import <Foundation/Foundation.h>
#interface SomeClass : NSObject {
NSString *_privateName;
}
#end
#implementation SomeClass
- (id)init {
self = [super init];
if (self) _privateName = #"Steve";
return self;
}
#end
NSString *nameGetter(id self, SEL _cmd) {
Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
return object_getIvar(self, ivar);
}
void nameSetter(id self, SEL _cmd, NSString *newName) {
Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
id oldName = object_getIvar(self, ivar);
if (oldName != newName) object_setIvar(self, ivar, [newName copy]);
}
int main(void) {
#autoreleasepool {
objc_property_attribute_t type = { "T", "#\"NSString\"" };
objc_property_attribute_t ownership = { "C", "" }; // C = copy
objc_property_attribute_t backingivar = { "V", "_privateName" };
objc_property_attribute_t attrs[] = { type, ownership, backingivar };
class_addProperty([SomeClass class], "name", attrs, 3);
class_addMethod([SomeClass class], #selector(name), (IMP)nameGetter, "##:");
class_addMethod([SomeClass class], #selector(setName:), (IMP)nameSetter, "v#:#");
id o = [SomeClass new];
NSLog(#"%#", [o name]);
[o setName:#"Jobs"];
NSLog(#"%#", [o name]);
}
}
Its (trimmed) output:
Steve
Jobs
The getter and setter methods should be written more carefully but this should be enough as an example of how to dynamically add a formal property at runtime.
If you take a look at NSKeyValueCoding protocol, documented here, you can see that there is a message called:
- (id)valueForUndefinedKey:(NSString *)key
You should override that method to provide your custom result for the specified undefined property. Of course this assumes that your class uses the corresponding protocol.
This kind of approach is commonly uses to provide unknown behavior to classes (eg. a selector that doesn't exist).
#properties - no (i.e. using dot syntax etc). But you can add storage using using associated objects: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?.
Related
I'm building a class that sets properties of a subclass dynamically at runtime from a plist, that works like this:
Example
You declare your properties in a subclass to match the names of keys:
#import "PlistModel.h"
#interface CustomModel : PlistModel
#property (strong, nonatomic) NSString * StringPropertyKey;
#property (strong, nonatomic) NSDate * DatePropertyKey;
#property (strong, nonatomic) NSArray * ArrayPropertyKey;
#property (strong, nonatomic) NSDictionary * DictionaryPropertyKey;
#property int IntPropertyKey;
#property BOOL BoolPropertyKey;
#property float FloatPropertyKey;
#end
That's it! The values are automatically populated at runtime without any additional code:
[CustomModel plistNamed:#"CustomModel" inBackgroundWithBlock:^(PlistModel *plistModel) {
CustomModel * customModel = (CustomModel *)plistModel;
NSLog(#"StringProperty: %#", customModel.StringPropertyKey);
NSLog(#"DateProperty: %#", customModel.DatePropertyKey);
NSLog(#"ArrayProperty: %#", customModel.ArrayPropertyKey);
NSLog(#"DictionaryProperty: %#", customModel.DictionaryPropertyKey);
NSLog(#"IntProperty: %i", customModel.IntPropertyKey);
NSLog(#"BoolProperty: %#", customModel.BoolPropertyKey ? #"YES" : #"NO");
NSLog(#"FloatProperty: %f", customModel.FloatPropertyKey);
}];
Problem
I set the properties at runtime by generating a selector and calling it with the value I want to set like this:
SEL propertySetterSelector = NSSelectorFromString(#"set<#PropertyName#>:");
void (*func)(id, SEL, id) = (void *)imp;
func(self, propertySetterSelector, objectToSet);
But, if for some reason a property is readonly the selector won't exist, so I'm looking for an alternative. I've found a way to identify that a property is readonly here:
- (NSMutableArray *) getPropertyNames {
// Prepare Package
NSMutableArray * propertyNames = [NSMutableArray array];
// Fetch Properties
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
// Parse Out Properties
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
// NSLog(#"Name: %s", name);
const char * attributes = property_getAttributes(property);
NSLog(#"Attributes: %s", attributes);
NSString * attributeString = [NSString stringWithUTF8String:attributes];
NSArray * attributesArray = [attributeString componentsSeparatedByString:#","];
if ([attributesArray containsObject:#"R"]) {
// is ReadOnly
NSLog(#"%s is read only", name);
// -- CAN I SET THE PROPERTY HERE? -- //
// property = #"Set"; ?
}
// Add to our array
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
// Free our properties
free(properties);
// Send it off
return propertyNames;
}
Perhaps if there's a way to set a objc_property_t ref directly.
Update
Through comments, I've realized there's some confusion. I think the core of my question is whether or not its possible to set an unknown property at runtime another way besides calling the selector like I'm doing.
Resources
Full Project: Here!
CodeReview Post that prompted this question: Here!
SetValue: forKey: Update
I have a readonly property:
#property (readonly) NSString * readOnlyProperty;
I declare this in the class:
+ (BOOL) accessInstanceVariablesDirectly {
return YES;
}
I call this:
[self setValue:#"HI" forKey:[NSString stringWithUTF8String:name]];
// -- OR -- //
[self setValue:#"HI" forKey:[NSString stringWithFormat:#"_%#",[NSString stringWithUTF8String:name]]];
Either way, value is still null.
UPDATE
You can try using setValue:forKey: for a readonly property, if the target class has defines the class method accessInstanceVariablesDirectly to return YES and if the property stores its value in a conventionally-named instance variable. See “ Default Search Pattern for setValue:forKey:” in the Key-Value Coding Programming Guide. KVC will unbox a primitive value if necessary.
ORIGINAL
It is not possible to set a property except by calling the property's setter, because a property is defined as a getter and optionally a setter. You can use the setValue:forKey: method of Key-Value Coding (KVC), and that's simpler and more reliably than constructing the setter name yourself, but under the covers that still calls the property's setter.
It is possible to set an instance variable using the Objective-C runtime. Look at the class_getInstanceVariable, object_setInstanceVariable, and object_setIvar methods.
You can guess that a property's value is stored in an instance variable whose name is the property named with an _ prefix. However, this is only a convention. The compiler uses the convention for auto-synthesized properties, but the compiler does not enforce the convention.
I have the following class in my iOS application (it is like an abstract class from the Java world).
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return _dictionary;
}
...
#end
I then have multiple classes which implement this above WSObject with the class method dictionary. The problem is, that each of these classes should have their own _dictionary, but they are all sharing the same object from the super class. I could, of course, copy to all the subclasses, but that would break the reusability. Besides this getter, there are other class methods in WSObject which mutate the dictionary. Because of this, there would be a several class methods which should be in every subclass.
How can I solve this in a smart way? Please tell me if my description is insufficient.
Associative references seem like they'll do the trick. You can essentially tack some storage on to the class object itself. (I'm using NSStrings here, in place of the dictionaries you want to use, just for demonstration.)
Superclass:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Stuper : NSObject
// Accessor method for the "class variable"
+ (NSString *) str;
// Analog to your +localStorePath
+ (NSString *) quote;
#end
#import "Stuper.h"
// The doc suggests simply using the address of a static variable as the key.
// This works fine, even though every class is (as in your problem) using
// the same key, because we are associating to a different class each time.
static char key;
#implementation Stuper
+ (NSString *) str {
NSString * s = objc_getAssociatedObject(self, &key);
if( !s ){
s = [self quote];
// You'll probably want to use OBJC_ASSOCIATION_RETAIN for your dictionary.
// self inside a class method is the class object; use that as
// the associator. The string is now tied to the associator, i.e.,
// has the same lifetime.
objc_setAssociatedObject(self, &key, s, OBJC_ASSOCIATION_COPY);
}
return s;
}
+ (NSString *) quote {
return #"It was the best of times, it was the worst of times.";
}
#end
Subclass:
#import "Stuper.h"
#interface Stub : Stuper #end
#import "Stub.h"
#implementation Stub
+ (NSString *) quote {
return #"Call me Ishmael.";
}
#end
Trying this out:
#import <Foundation/Foundation.h>
#import "Stuper.h"
#import "Stub.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"%#", [Stuper str]);
NSLog(#"%#", [Stub str]);
[pool drain];
return 0;
}
Each class object now has its own string, associated with it.
2011-12-05 23:11:09.031 SubClassVariables[36254:903] It was the best of times, it was the worst of times.
2011-12-05 23:11:09.034 SubClassVariables[36254:903] Call me Ishmael.
The only downside here is that you'll have to call the accessor method every time you want the object; you don't have a pointer you can use directly. You can call objc_getAssociatedObject in the superclass as an accessor, too, of course, since it has access to key.
In order to give each subclass its own dictionary, store a second dictionary object in your primary dictionary using the class name as the key. For example:
static NSMutableDictionary *_dictionary = nil;
+ (NSDictionary*)dictionary
{
if (_dictionary == nil)
_dictionary = [[NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]] mutableCopy];
NSString *key = NSStringFromClass( [self class] );
if ( [_dictionary objectForKey:key] == nil )
[_dictionary setObject:[NSMutableDictionary dictionary] forKey:key];
return [_dictionary objectForKey:key];
}
Perhaps you can return a copy of the dictionary
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return [_dictionary copy];
}
...
#end
Keep in mind that if you modify _dictionary you will get a copy of that modified dictionary which may differ from what is on disk.
How often is this being called? is it really necessary to cache the file contents in this static _dictionary object?
Why not just fetch it every time form disk, assuming it isn't too often that performance comes into question.
#implementation WSObject
+(NSDictionary*) dictionary {
return [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
...
#end
I have a class Film, each of which stores a unique ID. In C#, Java etc I can define a static int currentID and each time i set the ID i can increase the currentID and the change occurs at the class level not object level. Can this be done in Objective-C? I've found it very hard to find an answer for this.
Issue Description:
You want your ClassA to have a ClassB class variable.
You are using Objective-C as programming language.
Objective-C does not support class variables as C++ does.
One Alternative:
Simulate a class variable behavior using Objective-C features
Declare/Define an static variable within the classA.m so it will be only accessible for the classA methods (and everything you put inside classA.m).
Overwrite the NSObject initialize class method to initialize just once the static variable with an instance of ClassB.
You will be wondering, why should I overwrite the NSObject initialize method. Apple documentation about this method has the answer: "The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.)".
Feel free to use the static variable within any ClassA class/instance method.
Code sample:
file: classA.m
static ClassB *classVariableName = nil;
#implementation ClassA
...
+(void) initialize
{
if (! classVariableName)
classVariableName = [[ClassB alloc] init];
}
+(void) classMethodName
{
[classVariableName doSomething];
}
-(void) instanceMethodName
{
[classVariableName doSomething];
}
...
#end
References:
Class variables explained comparing Objective-C and C++ approaches
As of Xcode 8, you can define class properties in Obj-C. This has been added to interoperate with Swift's static properties.
Objective-C now supports class properties, which interoperate with Swift type properties. They are declared as: #property (class) NSString *someStringProperty;. They are never synthesized. (23891898)
Here is an example
#interface YourClass : NSObject
#property (class, nonatomic, assign) NSInteger currentId;
#end
#implementation YourClass
static NSInteger _currentId = 0;
+ (NSInteger)currentId {
return _currentId;
}
+ (void)setCurrentId:(NSInteger)newValue {
_currentId = newValue;
}
#end
Then you can access it like this:
YourClass.currentId = 1;
val = YourClass.currentId;
Here is a very interesting explanatory post I used as a reference to edit this old answer.
2011 Answer: (don't use this, it's terrible)
If you really really don't want to declare a global variable, there another option, maybe not very orthodox :-), but works... You can declare a "get&set" method like this, with an static variable inside:
+ (NSString*)testHolder:(NSString*)_test {
static NSString *test;
if(_test != nil) {
if(test != nil)
[test release];
test = [_test retain];
}
// if(test == nil)
// test = #"Initialize the var here if you need to";
return test;
}
So, if you need to get the value, just call:
NSString *testVal = [MyClass testHolder:nil]
And then, when you want to set it:
[MyClass testHolder:testVal]
In the case you want to be able to set this pseudo-static-var to nil, you can declare testHolder as this:
+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
static NSString *test;
if(shouldSet) {
if(test != nil)
[test release];
test = [_test retain];
}
return test;
}
And two handy methods:
+ (NSString*)test {
return [MyClass testHolderSet:NO newValue:nil];
}
+ (void)setTest:(NSString*)_test {
[MyClass testHolderSet:YES newValue:_test];
}
Hope it helps! Good luck.
On your .m file, you can declare a variable as static:
static ClassName *variableName = nil;
Then you can initialize it on your +(void)initialize method.
Please note that this is a plain C static variable and is not static in the sense Java or C# consider it, but will yield similar results.
In your .m file, declare a file global variable:
static int currentID = 1;
then in your init routine, refernce that:
- (id) init
{
self = [super init];
if (self != nil) {
_myID = currentID++; // not thread safe
}
return self;
}
or if it needs to change at some other time (eg in your openConnection method), then increment it there. Remember it is not thread safe as is, you'll need to do syncronization (or better yet, use an atomic add) if there may be any threading issues.
As pgb said, there are no "class variables," only "instance variables." The objective-c way of doing class variables is a static global variable inside the .m file of the class. The "static" ensures that the variable can not be used outside of that file (i.e. it can't be extern).
Here would be an option:
+(int)getId{
static int id;
//Do anything you need to update the ID here
return id;
}
Note that this method will be the only method to access id, so you will have to update it somehow in this code.
(Strictly speaking not an answer to the question, but in my experience likely to be useful when looking for class variables)
A class method can often play many of the roles a class variable would in other languages (e.g. changed configuration during tests):
#interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
#end
#implementation
+ (NSString*)theNameThing { return #"Something general"; }
- (void)doTheThing {
[SomeResource changeSomething:[self.class theNameThing]];
}
#end
#interface MySpecialCase: MyCls
#end
#implementation
+ (NSString*)theNameThing { return #"Something specific"; }
#end
Now, an object of class MyCls calls Resource:changeSomething: with the string #"Something general" upon a call to doTheThing:, but an object derived from MySpecialCase with the string #"Something specific".
u can rename the class as classA.mm and add C++ features in it.
Another possibility would be to have a little NSNumber subclass singleton.
What it says on the tin: I'd like to use the #property/#synthesize syntax to define a property on my Objective-C 2.0 class, but I want to place restrictions on the range of values allowed in the property. For example:
#interface MyClass : NSObject {
int myValue;
}
#property (nonatomic) int myValue;
Implementation:
#implementation MyClass
#synthesize myValue(test='value >= 0');
Note that the syntax here is just an example. Is this, or something much like it possible? Alternately, what is the literal equivalent of a synthesized setter, so that I can ensure that I use the same object retention rules in my manual setters as is used in a synthesized one.
Assuming your properties are Key-Value compliant (as they would be if you are using #synthesize) you should also implement Key-Value compliant validators. Take a look at Apple's documentation on the matter: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/Validation.html
The important thing to note is that validation does not happen automatically except when using certain kinds of binding. You either call the validator directly or by calling validateValue:forKey:error:.
You could override the produced setter to call the validator before saving it but if you are using bindings this is probably not what you want to do as the validator will possibly be called more than once for a single modification.
Also note that the validator might change the value being validated.
So lets look at your example (untested, btw. I'm not near a Mac):
#implementation MyClass
#synthesize myValue;
-(BOOL)validateMyValue:(id *)ioValue error:(NSError **)outError
{
if (*ioValue == nil) {
// trap this in setNilValueForKey
// alternative might be to create new NSNumber with value 0 here
return YES;
}
if ( [*ioValue intValue] < 0 ) {
NSString *errorString = #"myValue must be greater than zero";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
NSError *error = [[[NSError alloc] initWithDomain:#"MyValueError"
code:0
userInfo:userInfoDict] autorelease];
*outError = error;
return NO;
} else {
return YES;
}
}
If you wanted to override the synthesised setter and make it do the validation (still untested):
- (void)setMyValue:(int)value {
id newValue = [NSNumber numberWithInt:value];
NSError *errorInfo = nil;
if ( [self validateMyValue:&newValue error:&errorInfo] ) {
myValue = [newValue intValue];
}
}
You can see we had to wrap the integer in an NSNumber instance to do this.
When you use the #synthesize the accessor methods are generated. You can implement your own which will overwrite the generated one.
You can put your own implementation inside the accessor methods, e.g. you can add value checking before assignment and so on.
You can ommit one or the other or both, the ones that you don't implement will be generated because of #synthesize, if you use #dynamic you are specifying that you will provide accessors either at compile or run time.
Accessors will have names derived from the property name myproperty and setMyproperty. The method signatures are standard so it is easy to implement your own. The actual implementation depends on property definition (copy, retain, assign) and if it is read-only or not (read-only doesn't get set accessor). For more details see objective-c reference.
Apple reference:
#synthesize You use the #synthesize
keyword to tell the compiler that it
should synthesize the setter and/or
getter methods for the property if you
do not supply them within the
#implementation block.
#interface MyClass : NSObject
{
NSString *value;
}
#property(copy, readwrite) NSString *value;
#end
#implementation MyClass
#synthesize value;
- (NSString *)value {
return value;
}
- (void)setValue:(NSString *)newValue {
if (newValue != value) {
value = [newValue copy];
}
}
#end
I understand the basic syntax of Objective-C, have installed Cygwin, and want to experiment. However i am unsure of two things:
What i would import, and
what the primitive type names are.
Can someone help me?
The only object you can inherit from is called Object. Bare in mind that this offers nowhere near the same amount of functionality as NeXTStep's or Cocoa's NSObject. Object does not even have anything like reference counting. In order to get the same sort of reference counting memory management that NSObject has you'll need to implement it yourself.
#import <objc/Object.h>
#interface MyObject : Object
{
int retainCount;
}
- (id) retain;
- (int) retainCount;
- (void) release;
#end
#implementation MyObject
+ (MyObject *) alloc
{
// In Cocoa, allocated objects have an implicit retain.
MyObject *anObject = [super alloc];
anObject->retainCount = 1;
return anObject;
}
- (void) release
{
retainCount--;
if (retainCount == 0)
[self free];
}
- (id) retain
{
retainCount++;
return self;
}
- (int) retainCount
{
return retainCount;
}
#end
int main (int argc, char *argv[])
{
MyObject *test = [[MyObject alloc] init];
[test retain];
[test release];
[test release];
// (test should be deallocated now)
return 0;
}
When linking, you have to make sure you link with -lobjc, this is where the definition of Object lies (I think).
The other big catch is with static string instances, i.e. strings in code that appear #"like this". With the GNU runtime, static instances of strings need to have a particular ivar layout, which is:
// Let's assume that we have a protocol <MyObject> that defines all the basic methods
// like retain, release etc. In this case, these should be no-ops because the static
// string is never deallocated. In Cocoa, there is a protocol <NSObject> which provides
// the same common methods.
#interface MyStaticStringClass : Object <MyObject>
{
char *str;
unsigned len;
}
- (const char *) cString;
#end
#implementation MyStaticStringClass
- (void) retain
{
return;
}
- (id) retain
{
return self;
}
- (int) retainCount
{
return INT_MAX;
}
- (const char *) cString
{
return str;
}
#end
int main (int argc, char *argv[])
{
id aString = #"Hello world!";
fprintf (stdout, "aString has the contents: %s\n", [aString cString]);
return 0;
}
When compiling, you can use the flag -fconstant-string-class=MyStaticStringClass. You can provide whatever methods you like for the string class but it must have only two ivars and they must be in the right order. If you don't want to use Objective-C style strings, then you don't have to define a static string class. If you do define a static string class it should be able to replicate the behaviour of your dynamic string class (i.e. string objects that are allocated during run time) so that you can use either in a given situation.
For command-line utilities and basic apps I choose not to use Cocoa or GNUstep but rather define my own classes. It has many drawbacks, but I find that object abstraction and metamorphism in Objective-C is much easier to implement than in the other languages that I program in.