ObjectiveC中variables位置的声明/定义?

自从开始在iOS应用程序和目标C上工作以来,我一直困惑于可以声明和定义variables的不同位置。 一方面,我们有传统的C方法,另一方面,我们有新的ObjectiveC指令,在其上添加OO。 你们可以帮助我理解最好的实践和情况,我想用这些位置来表示我的变数,也许正确的理解我现在的想法吗?

下面是一个示例类(.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 

 #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 
  • 我对1和4的理解是这些是基于C风格的基于文件的声明和定义,它们不了解类的概念,因此必须准确使用它们如何在C中使用。我见过它们之前用于实现基于静态variables的单例。 我还有其他方便的用途吗?
  • 我和iOS一起工作的地方是ivars已经完全停止在@synthesize指令之外,因此可以被忽略。 是这样吗?
  • 关于5:为什么我会想要在私有接口中声明方法? 我的私人类方法似乎编译得很好,没有在接口声明。 这主要是为了可读性吗?

谢谢你们,伙计们!

我可以理解你的困惑。 特别是最近更新Xcode和新的LLVM编译器改变了Ivars和属性的声明方式。

在“现代”Objective-C(在“旧”Obj-C 2.0)之前,你没有太多的select。 实例variables用于在大括号{ }之间的标题中声明:

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

您只能在您的实现中访问这些variables,而不能从其他类访问这些variables。 要做到这一点,你必须声明访问器方法,看起来像这样:

 // 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 

这样,您就可以从其他类中获取和设置该实例variables,并使用通常的方括号语法来发送消息(调用方法):

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

由于手动声明和实现每个访问器方法都非常烦人,因此引入了@property@synthesize来自动生成访问器方法:

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

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

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

由于Xcode 4.4,你不必自己声明一个实例variables,你也可以跳过@synthesize 。 如果你没有声明一个ivar,编译器会为你添加它,它也会生成访问器方法,而不必使用@synthesize

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

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

这将完全按照上面的代码工作。 为了兼容性的原因,你仍然可以在头文件中声明ivars。 但是,因为你想这样做(而不是声明一个属性)的唯一原因是创build一个私有variables,你现在可以在实现文件中这样做,这是首选的方法。

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

 @property (nonatomic, readonly) myReadOnlyVar; 

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

至于完全在@interface@implementation块之外声明variables,是的,这些是简单的Cvariables,工作原理是一样的。

首先阅读@ DrummerB的回答。 这是一个很好的概述,你通常应该做什么。 考虑到这一点,针对您的具体问题:

 #import <Foundation/Foundation.h> // 1) What do I declare here? 

这里没有实际的variables定义(如果你确切地知道你在做什么,在技术上是合法的,但从来不这样做)。 你可以定义几种其他types的东西:

  • typdefs
  • 枚举
  • 实习医生

Externs看起来像variables声明,但它只是一个承诺,实际上在别的地方宣布它。 在ObjC中,它们只应该用来声明常量,而且通常只是string常量。 例如:

 extern NSString * const MYSomethingHappenedNotification; 

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

 NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification"; 

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

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


 // 3) class-specific method / property declarations @end 

是的。


 #import "SampleClass.h" // 4) what goes here? 

外部常量,如上所述。 此外文件静态variables可以在这里。 这些与其他语言中的类variables相当。


 @interface SampleClass() // 5) private interface, can define private methods and properties here @end 

是的


 @implementation SampleClass { // 6) define ivars } 

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


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

一般你不应该@synthesize了。 铿锵(Xcode)会为你做,而你应该放过它。

在过去的几年中,情况变得非常简单。 副作用是现在有三个不同的时代(易碎ABI,非易碎ABI,非易碎ABI +自动合成)。 所以当你看到较旧的代码时,可能会有点混乱。 因此,简单的混乱:D

我也很新,所以希望我不要把任何东西搞砸。

1&4:C风格的全局variables:文件范围广。 两者之间的区别在于,由于它们是文件范围广的,所以第一个对于任何人来说都是可用的,而第二个则不是。

2:实例variables。 大多数实例variables都是通过使用属性的访问器进行合成和检索/设置的,因为它使内存pipe理变得简单易行,同时还提供了易于理解的点符号。

6:实施ivars是有点新的。 这是放置私有ivars的好地方,因为你只想公开头部需要的东西,但子类不会inheritance它们。

3&7:公共方法和属性声明,然后执行。

5:私人界面。 我总是使用私人界面,只要我能保持干净,创造一种黑匣子的效果。 如果他们不需要知道的话,把它放在那里。 我也是为了可读性,不知道是否有其他原因。

这是在Objective-C中声明的各种variables的一个例子。 variables名称表示其访问权限。

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

 #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 

请注意,iNotVisiblevariables不能从任何其他类中看到。 这是一个可见性问题,所以用@public@public声明它们不会改变它。

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

让我们尝试访问这些variables。

文件:Cow.h

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

File: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 

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

File:Cow.m(part 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 

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

文件:main.m

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

这打印

 iMadeVisible iMadeVisible2 iMadeVisible3 

请注意,我能够访问_iNotVisible2类是私有的后援ivar _iNotVisible2 。 在Objective-C中,所有variables都可以被读取或设置,即使那些被标记为@privatevariables也不例外。

我没有包含关联对象或Cvariables,因为它们是不同的鸟类。 至于Cvariables,在@interface X{}@implementation X{}之外定义的任何variables都是具有文件范围和静态存储的Cvariables。

我没有讨论内存pipe理属性,或者readonly / readwrite,getter / setter属性。