【问题标题】:Objective-C Static Class Level variablesObjective-C 静态类级别变量
【发布时间】:2010-11-06 23:41:16
【问题描述】:

我有一个类 Film,每个类都存储一个唯一的 ID。在 C#、Java 等中,我可以定义一个静态 int currentID,每次设置 ID 时,我都可以增加 currentID,并且更改发生在类级别而不是对象级别。这可以在Objective-C中完成吗?我发现很难找到答案。

【问题讨论】:

    标签: objective-c variables static


    【解决方案1】:

    问题描述

    1. 您希望 ClassA 具有 ClassB 类变量。
    2. 您正在使用 Objective-C 作为编程语言。
    3. Objective-C 不像 C++ 那样支持类变量。

    另一种选择

    使用 Objective-C 特性模拟类变量行为

    1. 在 classA.m 中声明/定义一个静态变量,以便它只能被 classA 方法(以及您放入 classA.m 中的所有内容)访问。

    2. 重写 NSObject 的初始化类方法,只用 ClassB 的实例初始化静态变量一次。

    3. 你会想,我为什么要覆盖 NSObject 的初始化方法。关于此方法的 Apple 文档给出了答案:“运行时恰好在程序中的每个类或从它继承的任何类从程序中发送其第一条消息之前一次向程序中的每个类发送初始化。(因此,该方法如果不使用该类,则可能永远不会被调用。)”。

    4. 在任何 ClassA 类/实例方法中随意使用静态变量。

    代码示例

    文件: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
    

    参考文献

    1. Class variables explained comparing Objective-C and C++ approaches

    【讨论】:

    • 你能在classA.m中有一个ClassA类型的静态变量吗?
    • 这可能是一个愚蠢的问题,但是释放内存呢?没关系,因为只要应用程序运行,它就必须存在?
    • @samiq,查看Objective-C: Why retain a static variable?。指向对象的指针不能被删除,但对象本身可以。您可能不想释放它,因为只要应用程序正在运行,您很可能希望它存在,但是如果您释放它会节省内存,所以如果您知道不再需要它,那么您应该释放它。
    • 如果initialize()保证只调用一次,为什么需要条件“if (!classVariableName)”?
    • @jamie, initialize 为每个类调用一次(超类在子类之前),但如果子类没有覆盖initialize,父类initialize 将被再次调用。因此,如果您不希望该代码执行两次,则需要一个守卫。请参阅 Apple 的 Objective-C 文档中的 Initializing a Class Object
    【解决方案2】:

    从 Xcode 8 开始,您可以在 Obj-C 中定义类属性。添加它是为了与 Swift 的静态属性进行互操作。

    Objective-C 现在支持与 Swift 类型属性互操作的类属性。它们被声明为:@property (class) NSString *someStringProperty;。它们永远不会被合成。 (23891898)

    这是一个例子

    @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
    

    然后你可以这样访问它:

    YourClass.currentId = 1;
    val = YourClass.currentId;
    

    这是一个非常有趣的explanatory post,我用作参考来编辑这个旧答案。


    2011 回答:(不要用这个,太可怕了)

    如果你真的不想声明一个全局变量,还有另一种选择,也许不是很正统:-),但是有效...你可以声明一个像这样的“get&set”方法,里面有一个静态变量:

    + (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;
    }
    

    所以,如果您需要获取值,只需调用:

    NSString *testVal = [MyClass testHolder:nil]
    

    然后,当你想设置它时:

    [MyClass testHolder:testVal]
    

    如果您希望能够将此伪静态变量设置为 nil,您可以将 testHolder 声明为:

    + (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
        static NSString *test;
    
        if(shouldSet) {
            if(test != nil)
                [test release];
            test = [_test retain];
        }
    
        return test;
    }
    

    还有两个方便的方法:

    + (NSString*)test {
        return [MyClass testHolderSet:NO newValue:nil];
    }
    
    + (void)setTest:(NSString*)_test {
        [MyClass testHolderSet:YES newValue:_test];
    }
    

    希望对您有所帮助!祝你好运。

    【讨论】:

    • 酷,但它不是真正的全局变量,因为它不能从其他.m 文件访问,我认为它在Class.m 文件中是“全局”的就可以了。跨度>
    【解决方案3】:

    在您的 .m 文件中,您可以将变量声明为静态:

    static ClassName *variableName = nil;
    

    然后你可以在你的 +(void)initialize 方法上初始化它。

    请注意,这是一个普通的 C 静态变量,并不是 Java 或 C# 所认为的静态变量,但会产生类似的结果。

    【讨论】:

      【解决方案4】:

      在您的 .m 文件中,声明一个文件全局变量:

      static int currentID = 1;
      

      然后在您的 init 例程中,参考:

      - (id) init
      {
          self = [super init];
          if (self != nil) {
              _myID = currentID++; // not thread safe
          }
          return self;
      }
      

      或者如果它需要在其他时间更改(例如在您的 openConnection 方法中),则在此处增加它。请记住,它不是线程安全的,如果可能存在任何线程问题,您需要进行同步(或者更好的是,使用原子添加)。

      【讨论】:

        【解决方案5】:

        正如 pgb 所说,没有“类变量”,只有“实例变量”。做类变量的objective-c方式是类的.m文件内的静态全局变量。 “静态”确保变量不能在该文件之外使用(即它不能是外部的)。

        【讨论】:

          【解决方案6】:

          这是一个选项:

          +(int)getId{
              static int id;
              //Do anything you need to update the ID here
              return id;
          }
          

          请注意,此方法将是访问 id 的唯一方法,因此您必须在此代码中以某种方式对其进行更新。

          【讨论】:

            【解决方案7】:

            (严格来说不是问题的答案,但根据我的经验,在查找类变量时可能很有用)

            类方法通常可以扮演类变量在其他语言中的许多角色(例如,在测试期间更改配置):

            @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
            

            现在,MyCls 类的对象在调用@"Something general" 时使用字符串@"Something general" 调用Resource:changeSomething:,但是从MySpecialCase 派生的对象使用字符串@"Something specific"

            【讨论】:

              【解决方案8】:

              您可以将类重命名为 classA.mm 并在其中添加 C++ 功能。

              【讨论】:

                【解决方案9】:

                另一种可能性是有一个小NSNumber 子类单例。

                【讨论】:

                  猜你喜欢
                  • 2012-01-26
                  • 2011-08-27
                  • 1970-01-01
                  • 2012-07-14
                  • 2011-01-04
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-04-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多