8

I can't seem to find a way to set the static int I have created to assign unique ids to every object I save to persistent memory. The following gives me a 'no setter method 'setIdGen' for assignment to property.

-(void)viewDidLoad
{
    PlayerMenuController.idGen = [[NSUserDefaults standardUserDefaults] floatForKey:@"idGen"];
}

As well as the above I've tried creating a static setIdGen method that would return bad access errors, and making NSIntegers with their own set methods. My static NSMutableArray gave the same errors when I tried to assign it using = but worked fine when using setArray.

idGen method:

+ (int) idGen
{
    /*static int idGen;
    if(!idGen)
    {
        idGen = 0;
        NSLog(@"idGen reset");
    }*/
    return self.idGen;
}
Declan McKenna
  • 4,321
  • 6
  • 54
  • 72

3 Answers3

43

Update 2017

Xcode 8 introduced class properties to Objective-C, from the release notes:

Objective-C now supports class properties, which interoperate with Swift type properties. They are declared as @property (class) NSString *someStringProperty;, and are never synthesised.

This means our sample interface below can become:

@interface PlayerMenuController : NSObject

@property (class) int idGen;

@end

However you must still implement the methods yourself, as shown below, as class properties are never synthesised. Note that this also means if you specify property attributes, such as copy, that your methods must implement the semantics.


Original Answer

It looks like you are trying to implement a class property, but there is not such thing in Objective-C - a property is a pair of instance methods.

However, you can fake it...

While the @property declaration is not available to you, if you declare class methods which follow the right naming convention then your compiler may (tested on Xcode 4.6.1, "may" as I cannot offhand point to this being supported, but it's simple to test and will compile time error if not) allow you to use dot notation, i.e. it looks like a class property even if it lacks an @property.

A sample interface:

@interface PlayerMenuController : NSObject

// a class "property"
+ (int) idGen;
+ (void) setIdGen:(int)value;

@end

The implementation:

@implementation PlayerMenuController

static int idGen = 0;

+ (int) idGen { return idGen; }
+ (void) setIdGen:(int)value { idGen = value; }

@end

And test it:

NSLog(@"initial value: %d", PlayerMenuController.idGen);
PlayerMenuController.idGen = 42;
NSLog(@"updated value: %d", PlayerMenuController.idGen);

producing:

initial value: 0
updated value: 42

So we have a "class property" - it looks, walks and quacks like a property ;-)

CRD
  • 52,522
  • 5
  • 70
  • 86
  • 1
    Actually, you can declare a property, then implement the setter/getter methods to operate on the static field. (Not that this is recommended.) – Hot Licks Apr 12 '13 at 20:32
  • If I'm reading the original question correctly, the OP is trying to use a global variable to provide a unique id for each instance, which is of course impossible. – jlehr Apr 12 '13 at 20:37
  • @HotLicks - The OP has written "class.property", hence my code. But you could be right, they might be after an instance property accessing a "class variable" (static)... – CRD Apr 12 '13 at 20:55
  • @jleher - I beg to differ, you can use a static variable to provide a unique id to every instance. It is by no means impossible, not even hard (as I've done it myself ;-)). But you don't need a "class property" to do it. – CRD Apr 12 '13 at 20:58
  • Not entirely sure what the difference between a global and class property is but this seem to work fine. I just needed something that could be kept independently from the instances so I can assign a unique id to each instance. Thanks – Declan McKenna Apr 15 '13 at 23:33
  • 1
    @Deco - a global property would be one which stands alone and is visible from everywhere, a class property would belong to a class and require reference via the class. Two different classes could have a class property with the same name, but you couldn't have two globals properties with the same name. – CRD Apr 16 '13 at 00:11
  • Hey I made a gist some time ago, not ARC compatible but it can actually be used in ARC by using the assign methods and specifying strong or weak references. It's a bit more self contained: https://gist.github.com/darionco/03649feeee57f86fee3f – Dario Sep 05 '14 at 03:36
3

If you use a static variable and you ever want to subclass this, the static variable will be the same for both parent and child, so any change when addressing the child class, will also change the same 'property' on it's parent. (and vice versa)

The safe way to do it is using objc/runtime.h associated objects

+(int) idGen
{
    NSNumber* idGen = objc_getAssociatedObject(self, @selector(idGen));

    if (idGen == nil)
    {
        idGen = @0;
    }
    else
    {
        idGen = @([idGen intValue] + 1);
    }

    self.idGen = [idGen intValue];

    return [idGen intValue];
}

+(void)setIdGen:(int)idGen
{
    objc_setAssociatedObject(self, @selector(idGen), @(idGen), OBJC_ASSOCIATION_RETAIN);
}
GreatWiz
  • 735
  • 4
  • 8
1

You shouldn't be returning self.idGen because an int is not a property. Your code should work if you do this:

 + (int) idGen {
     static int idGen;
     return idGen;
   }
Mike Z
  • 4,121
  • 2
  • 31
  • 53
  • 2
    `if (!idGen) idGen = 0;` is a no-op. It's one of a superfluous no-op even more since static variables are initialized to 0. –  Apr 12 '13 at 20:14
  • Yeah, you can just initialize scalar types immediately. Only objects need the `id obj = nil; if (!obj) obj = [[Obj alloc] init];` trick. – mipadi Apr 12 '13 at 20:15
  • @mipadi or anything you want to lazy-initialize to something other than zero. –  Apr 12 '13 at 20:16
  • Good point. I was thinking of a usage where you instantiate if it is `nil` – Mike Z Apr 12 '13 at 20:17
  • 1
    @H2CO3: When would you want to lazily initialize a scalar? – mipadi Apr 12 '13 at 20:17
  • @mipadi Not only scalars aren't objects. You can have C strings, structures, pointers, whatever. –  Apr 12 '13 at 20:18
  • @MikeZ I'm not sure what you meant by "...an `int` is not a property", but as written that makes no sense. – jlehr Apr 12 '13 at 20:39
  • @H2CO3: Good point re: pointers, but pointers aren't scalars. :) You can statically initialize a struct and C strings, too. Only things that can't be initialized statically HAVE to be lazily initialized. – mipadi Apr 12 '13 at 21:34
  • @mipadi I didn't say pointers were scalars. "Not only scalars aren't objects" - this implies exactly what I meant (and what is correct). –  Apr 12 '13 at 21:37
  • @H2CO3: Okay, sorry for the misunderstanding. My point is, you can statically initialize C strings and structs; object pointers are the few data types that cannot be statically initialized. – mipadi Apr 12 '13 at 22:09
  • @jlehr my line of "...an `int` is not a property" means that you cannot use an `int` with dot notation as the original poster was doing. Some people assume that certain data types are accessible with dot notation when actually only getters and setters of properties (declared properly) are accessible in dot notation. I point this out for people who are well versed in `C` but not in `Objective-C` because there are cases where you can use dot notation in `C` that are not properties, i.e. structs – Mike Z Apr 12 '13 at 22:28
  • @MikeZ Your statement is confusing. `int` is a data type. Obviously, a data type is not a property. I think what you meant to say is that a *variable* is not a property. Updating the wording of your answer would help clarify your point. – jlehr Apr 13 '13 at 14:28
  • When you are declaring variable inside `static` method then there is no need to make `static` variable. `Static`method's variables are also `static` by default. So it is okay - +(int) idGen { int idGen = 5; return idGen; } – TheTiger May 30 '13 at 09:39