【问题标题】:How do I declare class-level properties in Objective-C?如何在 Objective-C 中声明类级属性?
【发布时间】:2009-03-30 04:09:04
【问题描述】:

也许这很明显,但我不知道如何在 Objective-C 中声明类属性。

我需要为每个类缓存一个字典,并想知道如何将它放入类中。

【问题讨论】:

    标签: objective-c cocoa oop


    【解决方案1】:

    属性在Objective-C中具有特定含义,但我认为您的意思是等同于静态变量的东西?例如。所有类型的 Foo 都只有一个实例?

    要在 Objective-C 中声明类函数,请使用 + 前缀而不是 - 所以你的实现看起来像这样:

    // Foo.h
    @interface Foo {
    }
    
    + (NSDictionary *)dictionary;
    
    // Foo.m
    + (NSDictionary *)dictionary {
      static NSDictionary *fooDict = nil;
      if (fooDict == nil) {
        // create dict
      }
      return fooDict;
    }
    

    【讨论】:

    • 是这样吗?将 fooDict 设置为 nil 会不会因为字典方法的第一行总是导致每次都重新创建字典?
    • 行静态 NSDictionary *fooDict = nil;只会被执行一次!即使在多次调用的方法中,如果存在具有此名称的静态变量,则使用关键字 static 的声明(以及在此示例中的初始化)将被忽略。
    • @BenC.R.Leggiero 是的,绝对是。 .-accessor 语法不依赖于 Objective-C 中的属性,它只是任何方法的编译快捷方式,它返回一些东西而不使用任何参数。在这种情况下,我更喜欢它——我个人更喜欢 . 语法用于客户端代码打算获取某些东西,而不是执行操作 的任何用法(即使实现代码可能会创建一次,或者执行副作用行动)。大量使用 . 语法也会导致代码更具可读性:[…]s 的存在意味着当提取使用 . 语法时正在完成一些重要的事情。
    • 看看 Alex Nolasco 的回答,类属性自 Xcode 8 版本起可用:stackoverflow.com/a/37849467/6666611
    【解决方案2】:

    我正在使用这个解决方案:

    @interface Model
    + (int) value;
    + (void) setValue:(int)val;
    @end
    
    @implementation Model
    static int value;
    + (int) value
    { @synchronized(self) { return value; } }
    + (void) setValue:(int)val
    { @synchronized(self) { value = val; } }
    @end
    

    我发现它作为单例模式的替代品非常有用。

    要使用它,只需使用点符号访问您的数据:

    Model.value = 1;
    NSLog(@"%d = value", Model.value);
    

    【讨论】:

    • 这真的很酷。但是self 在类方法中到底是什么意思?
    • @ToddLehman selfthe object that's received the message。由于classes are also objects,在这种情况下self 表示Model
    • 为什么 getter 需要是 @synchronized
    • 这确实很酷,这很有效。您可以编写自己的类级别属性,这些属性与真实事物完全一样。我猜同步 self 相当于在你的属性声明中使用“原子”,如果你想要“非原子”版本可以省略吗?另外我会考虑用'_'命名支持变量,这是苹果的默认设置,并且还提高了可读性,因为从 getter/setter 返回/设置 self.value 会导致无限递归。在我看来,这是正确的答案。
    • 这很酷,但是... 10 行代码只是为了创建 1 个静态成员?真是个黑客。 Apple 应该将其作为一项功能。
    【解决方案3】:

    正如在 WWDC 2016/XCode 8 (what's new in LLVM session @5:05) 上看到的那样。类属性可以声明如下

    @interface MyType : NSObject
    @property (class) NSString *someString;
    @end
    
    NSLog(@"format string %@", MyType.someString);
    

    请注意,类属性永远不会合成

    @implementation
    static NSString * _someString;
    + (NSString *)someString { return _someString; }
    + (void)setSomeString:(NSString *)newString { _someString = newString; }
    @end
    

    【讨论】:

    • 也许应该明确说明这只是访问器声明的糖。正如您所指出的,该属性不是合成的: (static) 变量仍必须声明和使用,并且方法显式实现,就像以前一样。点语法以前也可以使用。总而言之,这听起来比实际更重要。
    • 最重要的是这意味着您可以在不使用 () 的情况下从 Swift 代码中访问单例,并且按照惯例删除了类型后缀。例如。 XYZMyClass.shared (Swift 3) 而不是 XYZMyClass.sharedMyClass()
    • 这看起来不是线程安全的。如果这可以从您代码中的不同线程中发生变异,我会确保您处理这将创建的潜在竞争条件。
    • 一个用于消费类的漂亮干净的接口,但它仍然需要大量的工作。用一个静态块试过这个,它不是很有趣。事实上,仅使用静态更容易。
    • 使用 dispatch_once 令牌可以很容易地为线程安全的“单例”行为增强实现 - 但否则解决方案正确地展示了答案
    【解决方案4】:

    如果您正在寻找 @property 的类级别等价物,那么答案是“没有这样的东西”。但请记住,无论如何,@property 只是语法糖;它只是创建适当命名的对象方法。

    您想创建访问静态变量的类方法,正如其他人所说,它们的语法略有不同。

    【讨论】:

    • 即使认为属性是语法的,如果能够将点语法用于诸如 MyClass.class 而不是 [MyClass class] 之类的东西仍然很好。
    • @ZakyGerman 你可以! UIDevice.currentDevice.identifierForVendor 为我工作。
    • @tc。谢谢!现在填傻了。出于某种原因,我确信我过去曾尝试过,但没有奏效。这是一个新功能吗?
    • @ZakyGerman 它已经为类方法工作了至少一两年。如果 getter/setter 方法具有预期的类型,我相信它总是适用于实例方法。
    【解决方案5】:

    这是一种线程安全的方法:

    // Foo.h
    @interface Foo {
    }
    
    +(NSDictionary*) dictionary;
    
    // Foo.m
    +(NSDictionary*) dictionary
    {
      static NSDictionary* fooDict = nil;
    
      static dispatch_once_t oncePredicate;
    
      dispatch_once(&oncePredicate, ^{
            // create dict
        });
    
      return fooDict;
    }
    

    这些编辑确保 fooDict 只创建一次。

    来自Apple documentation:“dispatch_once - 在应用程序的生命周期内只执行一次块对象。”

    【讨论】:

    • dispatch_once 代码是否无关紧要,因为静态 NSDictionary 可以在 +(NSDictionary*) 字典方法的第一行初始化,并且由于它是静态的,所以无论如何它只会被初始化一次?
    • @jcpennypincher 尝试在其静态声明的同一行初始化字典会产生以下编译器错误:Initializer element is not a compile-time constant
    • @GeorgeWS 您只会收到该错误,因为您试图将其初始化为函数的结果(alloc 和 init 是函数)。如果你将它初始化为 nil,然后添加 if(obj==nil) 并在那里初始化,你会没事的。
    • Rob,这不是线程安全的。这里提供的代码是最好的。
    • 我最喜欢这个答案,因为它是完整且线程安全的 - 但是,它只会更好地处理实现,而操作的问题是关于类属性的声明 - 而不是它们的实现(有与实现无关)。无论如何,谢谢。
    【解决方案6】:

    从 Xcode 8 开始,Objective-C 现在支持类属性:

    @interface MyClass : NSObject
    @property (class, nonatomic, assign, readonly) NSUUID* identifier;
    @end
    

    由于永远不会合成类属性,因此您需要编写自己的实现。

    @implementation MyClass
    static NSUUID*_identifier = nil;
    
    + (NSUUID *)identifier {
      if (_identifier == nil) {
        _identifier = [[NSUUID alloc] init];
      }
      return _identifier;
    }
    @end
    

    您可以使用类名上的普通点语法访问类属性:

    MyClass.identifier;
    

    【讨论】:

      【解决方案7】:

      属性仅在对象中具有值,在类中没有。

      如果你需要为一个类的所有对象存储一些东西,你必须使用一个全局变量。您可以通过在实现文件中声明 static 来隐藏它。

      您还可以考虑使用对象之间的特定关系:您将主角色的角色分配给您类的特定对象,并将其他对象链接到该主对象。主人将把字典作为一个简单的财产。我想到了一棵树,就像 Cocoa 应用程序中用于视图层次结构的树。

      另一种选择是创建一个专用类的对象,该类由您的“类”字典和与该字典相关的所有对象组成。这类似于 Cocoa 中的 NSAutoreleasePool

      【讨论】:

        【解决方案8】:

        从 Xcode 8 开始,您可以使用 Berbie 回答的 class 属性。

        但是,在实现中,您需要使用静态变量代替 iVar 为类属性定义类 getter 和 setter。

        示例.h

        @interface Sample: NSObject
        @property (class, retain) Sample *sharedSample;
        @end
        

        示例.m

        @implementation Sample
        static Sample *_sharedSample;
        + ( Sample *)sharedSample {
           if (_sharedSample==nil) {
              [Sample setSharedSample:_sharedSample];
           }
           return _sharedSample;
        }
        
        + (void)setSharedSample:(Sample *)sample {
           _sharedSample = [[Sample alloc]init];
        }
        @end
        

        【讨论】:

          【解决方案9】:

          如果你有很多类级别的属性,那么单例模式可能是合适的。 像这样的:

          // Foo.h
          @interface Foo
          
          + (Foo *)singleton;
          
          @property 1 ...
          @property 2 ...
          @property 3 ...
          
          @end
          

          // Foo.m
          
          #import "Foo.h"
          
          @implementation Foo
          
          static Foo *_singleton = nil;
          
          + (Foo *)singleton {
              if (_singleton == nil) _singleton = [[Foo alloc] init];
          
              return _singleton;
          }
          
          @synthesize property1;
          @synthesize property2;
          @synthesise property3;
          
          @end
          

          现在访问您的类级属性,如下所示:

          [Foo singleton].property1 = value;
          value = [Foo singleton].property2;
          

          【讨论】:

          • 这个单例实现不是线程安全的,不要使用它
          • 当然它不是线程安全的,你是第一个提到线程安全的人,默认情况下,非线程安全上下文(即单线程)中的线程安全重载毫无意义。
          • 在这里使用dispatch_once 会很容易。
          • PO 想要在声明方面得到答案,而不是在实现方面 - 甚至建议的实现也不完整(不是线程安全的)。
          【解决方案10】:

          [试试这个解决方案很简单] 你可以在 Swift 类中创建一个静态变量,然后从任何 Objective-C 类中调用它。

          【讨论】:

          • OP 没有问如何在 Swift 中创建静态属性,这并没有解决他的问题。
          • 或者更确切地说,它解决了问题,但没有回答问题。不确定这是否值得并投反对票...
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-03-21
          • 2012-07-12
          • 1970-01-01
          • 1970-01-01
          • 2018-05-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多