ios ObjectiveC中变量位置的声明/定义?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12632285/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 20:27:45  来源:igfitidea点击:

Declaration/definition of variables locations in ObjectiveC?

iphoneobjective-ciosios5

提问by Alexandr Kurilin

Ever since starting to work on iOS apps and objective C I've been really puzzled by the different locations where one could be declaring and defining variables. On one hand we have the traditional C approach, on the other we have the new ObjectiveC directives that add OO on top of that. Could you folks helps me understand the best practice and situations where I'd want to use these locations for my variables and perhaps correct my present understanding?

自从开始在iOS应用程序的工作和目标C我一直很不解,其中一个可以声明和定义变量的不同位置。一方面我们有传统的 C 方法,另一方面我们有新的 ObjectiveC 指令,在它之上添加 OO。你们能帮我理解最佳实践和我想将这些位置用于我的变量的情况,并可能纠正我目前的理解吗?

Here's a sample class (.h and .m):

这是一个示例类(.h 和 .m):

#import <Foundation/Foundation.h>

// 1) What do I declare here?

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

// 3) class-specific method / property declarations

@end

and

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

@implementation SampleClass
{
    // 6) define ivars
}

// 7) define methods and synthesize properties from both public and private
//    interfaces

@end
  • My understanding of 1 and 4 is that those are C-style file-based declarations and definitions that have no understanding whatsoever of the concept of class, and thus have to be used exactly how they would be used in C. I've seen them used for implementing static variable-based singletons before. Are there other convenient uses I'm missing?
  • My take from working with iOS is that ivars have been alost completely phased out outside of the @synthesize directive and thus can be mostly ignored. Is that the case?
  • Regarding 5: why would I ever want to declare methods in private interfaces? My private class methods seem to compile just fine without a declaration in the interface. Is it mostly for readability?
  • 我对 1 和 4 的理解是,那些是 C 风格的基于文件的声明和定义,它们对类的概念一无所知,因此必须准确地使用它们在 C 中的使用方式。我见过它们之前用于实现基于静态变量的单例。我还缺少其他方便的用途吗?
  • 我对 iOS 工作的看法是 ivars 几乎完全被淘汰出@synthesize 指令之外,因此几乎可以被忽略。是这样吗?
  • 关于 5:为什么我要在私有接口中声明方法?我的私有类方法似乎在没有接口声明的情况下编译得很好。主要是为了可读性吗?

Thanks a bunch, folks!

非常感谢,伙计们!

回答by DrummerB

I can understand your confusion. Especially since recent updates to Xcode and the new LLVM compiler changed the way ivars and properties can be declared.

我能理解你的困惑。特别是因为最近对 Xcode 的更新和新的 LLVM 编译器改变了 ivars 和属性的声明方式。

Before "modern" Objective-C (in "old" Obj-C 2.0) you didn't have a lot of choices. Instance variables used to be declared in the header between the curly brackets { }:

在“现代”Objective-C(在“旧”Obj-C 2.0 中)之前,您没有很多选择。以前在大括号之间的标题中声明的实例变量{ }

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

You were able to access these variables only in your implementation, but not from other classes. To do that, you had to declare accessor methods, that look something like this:

您只能在您的实现中访问这些变量,而不能从其他类访问。为此,您必须声明访问器方法,如下所示:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

This way you were able to get and set this instance variable from other classes too, using the usual square bracket syntax to send messages (call methods):

通过这种方式,您也可以从其他类中获取和设置此实例变量,使用通常的方括号语法发送消息(调用方法):

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

Because manually declaring and implementing every accessor method was quite annoying, @propertyand @synthesizewere introduced to automatically generate the accessor methods:

因为手动声明和实现每个访问器方法很烦人,@property并且@synthesize被引入以自动生成访问器方法:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

The result is much clearer and shorter code. The accessor methods will be implemented for you and you can still use the bracket syntax as before. But in addition, you can also use the dot syntax to access properties:

结果是更清晰和更短的代码。访问器方法将为您实现,您仍然可以像以前一样使用括号语法。但除此之外,您还可以使用点语法来访问属性:

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Since Xcode 4.4 you don't have to declare an instance variable yourself anymore and you can skip @synthesizetoo. If you don't declare an ivar, the compiler will add it for you and it will also generate the accessor methods without you having to use @synthesize.

从 Xcode 4.4 开始,您不必再自己声明实例变量,也可以跳过@synthesize。如果您不声明 ivar,编译器会为您添加它,并且它还会生成访问器方法,而您无需使用@synthesize.

The default name for the automatically generated ivar is the name or your property starting with an underscore. You can change the generated ivar's name by using @synthesize myVar = iVarName;

自动生成的 ivar 的默认名称是以下划线开头的名称或您的属性。您可以使用以下方法更改生成的 ivar 的名称@synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

This will work exactly as the code above. For compatibility reasons you can still declare ivars in the header. But because the only reason why you would want to do that (and not declare a property) is to create a private variable, you can now do that in the implementation file as well and this is the preferred way.

这将与上面的代码完全一样。出于兼容性原因,您仍然可以在标头中声明 ivars。但是因为您想要这样做(而不是声明属性)的唯一原因是创建一个私有变量,所以您现在也可以在实现文件中这样做,这是首选方式。

An @interfaceblock in the implementation file is actually an Extensionand can be used to forward declare methods (not needed anymore) and to (re)declare properties. You could for instance declare a readonlyproperty in your header.

@interface实现文件中的块实际上是一个扩展,可用于转发声明方法(不再需要)和(重新)声明属性。例如,您可以readonly在标题中声明一个属性。

@property (nonatomic, readonly) myReadOnlyVar;

and redeclare it in your implementation file as readwriteto be able to set it using the property syntax and not only via direct access to the ivar.

并在您的实现文件中重新声明它,readwrite以便能够使用属性语法来设置它,而不仅仅是通过直接访问 ivar。

As for declaring variables completely outside of any @interfaceor @implementationblock, yes those are plain C variables and work exactly the same.

至于完全在 any@interface@implementationblock之外声明变量,是的,那些是普通的 C 变量并且工作完全相同。

回答by Rob Napier

First, read @DrummerB's answer. It a good overview of the whys and what you should generally do. With that in mind, to your specific questions:

首先,阅读@DrummerB 的回答。它的个为什么的一个很好的概述和你一般应该做的。考虑到这一点,对于您的具体问题:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

No actual variable definitions go here (it's technically legal to do so if you know exactly what you're doing, but never do this). You may define several other kinds of things:

这里没有实际的变量定义(如果您确切地知道自己在做什么,那么这样做在技术上是合法的,但永远不要这样做)。您可以定义几种其他类型的东西:

  • typdefs
  • enums
  • externs
  • 类型定义
  • 枚举
  • 外援

Externs look like variable declarations, but they're just a promise to actually declare it somewhere else. In ObjC, they should only be used to declare constants, and generally only string constants. For instance:

Externs 看起来像变量声明,但它们只是承诺在其他地方实际声明它。在 ObjC 中,它们应该只用于声明常量,一般只用于字符串常量。例如:

extern NSString * const MYSomethingHappenedNotification;

You would then in your .mfile declare the actual constant:

然后你会在你的.m文件中声明实际的常量:

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";


@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

As noted by DrummerB, this is legacy. Don't put anything here.

正如 DrummerB 所指出的,这是遗留问题。不要在这里放任何东西。



// 3) class-specific method / property declarations

@end

Yep.

是的。



#import "SampleClass.h"

// 4) what goes here?

External constants, as described above. Also file static variables can go here. These are the equivalent of class variables in other languages.

外部常数,如上所述。文件静态变量也可以放在这里。这些相当于其他语言中的类变量。



@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

Yep

是的



@implementation SampleClass
{
    // 6) define ivars
}

But very rarely. Almost always you should allow clang (Xcode) to create the variables for you. The exceptions are usually around non-ObjC ivars (like Core Foundation objects, and especially C++ objects if this is an ObjC++ class), or ivars that have weird storage semantics (like ivars that don't match with a property for some reason).

但很少。几乎总是你应该允许 clang (Xcode) 为你创建变量。例外通常是围绕非 ObjC ivars(例如 Core Foundation 对象,尤其是 C++ 对象,如果这是一个 ObjC++ 类),或者具有奇怪存储语义的 ivars(例如由于某种原因与属性不匹配的 ivars)。



// 7) define methods and synthesize properties from both public and private
//    interfaces

Generally you shouldn't @synthesize anymore. Clang (Xcode) will do it for you, and you should let it.

通常你不应该再@synthesize。Clang (Xcode) 会为你做这件事,你应该让它。

Over the last few years, things have gotten dramatically simpler. The side-effect is that there are now three different eras (Fragile ABI, Non-fragile ABI, Non-fragile ABI + auto-syntheisze). So when you see the older code, it can be a little confusing. Thus confusion arising from simplicity :D

在过去的几年里,事情变得非常简单。副作用是现在有三个不同的时代(脆弱 ABI、非脆弱 ABI、非脆弱 ABI + 自动合成)。因此,当您看到较旧的代码时,可能会有些困惑。因此,简单性引起的混乱:D

回答by Metabble

I'm also pretty new, so hopefully I don't screw anything up.

我也很新,所以希望我不会搞砸任何事情。

1 & 4: C-style global variables: they have file wide scope. The difference between the two is that, since they're file wide, the first will be available to anyone importing the header while the second is not.

1 & 4:C 风格的全局变量:它们具有文件范围。两者之间的区别在于,由于它们是文件宽的,第一个将可供任何导入标头的人使用,而第二个则不可用。

2: instance variables. Most instance variables are synthesized and retrieved/set through accessors using properties because it makes memory management nice and simple, as well as gives you easy-to-understand dot notation.

2:实例变量。大多数实例变量是通过使用属性的访问器合成和检索/设置的,因为它使内存管理变得简单而简单,并为您提供易于理解的点符号。

6: Implementation ivars are somewhat new. It's a good place to put private ivars, since you want to only expose what's needed in the public header, but subclasses don't inherit them AFAIK.

6:实现变量有点新。这是放置私有变量的好地方,因为您只想公开公共头文件中需要的内容,但子类不会继承它们 AFAIK。

3 & 7: Public method and property declarations, then implementations.

3 & 7:公共方法和属性声明,然后是实现。

5: Private interface. I always use private interfaces whenever I can to keep things clean and create a kind of black box effect. If they don't need to know about it, put it there. I also do it for readability, don't know if there are any other reasons.

5:私有接口。我总是尽可能使用私有接口来保持干净并创建一种黑匣子效果。如果他们不需要知道,就把它放在那里。我也是为了可读性做的,不知道还有没有别的原因。

回答by Jano

This is an example of all kinds of variables declared in Objective-C. The variable name indicate its access.

这是在 Objective-C 中声明的各种变量的示例。变量名称表示其访问权限。

File: Animal.h

文件:Animal.h

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

File: Animal.m

文件:Animal.m

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

Note that the iNotVisible variables are not visible from any other class. This is a visibility issue, so declaring them with @propertyor @publicdoesn't change it.

请注意,iNotVisible 变量在任何其他类中均不可见。这是一个可见性问题,因此声明它们@property@public不更改它。

Inside a constructor it's good practice to access variables declared with @propertyusing underscore instead selfto avoid side effects.

在构造函数中,最好访问@property使用下划线声明的变量self以避免副作用。

Let's try to access the variables.

让我们尝试访问变量。

File: Cow.h

文件:Cow.h

#import "Animal.h"
@interface Cow : Animal
@end

File: Cow.m

文件:Cow.m

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

We can still access the not visible variables using the runtime.

我们仍然可以使用运行时访问不可见的变量。

File: Cow.m (part 2)

文件:Cow.m(第 2 部分)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

Let's try to access the not visible variables.

让我们尝试访问不可见的变量。

File: main.m

文件:main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

This prints

这打印

iMadeVisible 
iMadeVisible2 
iMadeVisible3

Note that I was able to access the backing ivar _iNotVisible2which is private to the subclass. In Objective-C all variables can be read or set, even those that are marked @private, no exceptions.

请注意,我能够访问_iNotVisible2子类私有的支持 ivar 。在 Objective-C 中,所有变量都可以被读取或设置,即使是那些被标记的@private,也不例外。

I didn't include associated objects or C variables as they are different birds. As for C variables, any variable defined outside @interface X{}or @implementation X{}is a C variable with file scope and static storage.

我没有包括关联的对象或 C 变量,因为它们是不同的鸟。至于 C 变量,任何在外部定义的变量@interface X{}或者@implementation X{}是具有文件作用域和静态存储的 C 变量。

I didn't discuss memory management attributes, or readonly/readwrite, getter/setter attributes.

我没有讨论内存管理属性,或只读/读写、getter/setter 属性。