Objective-C实例变量?

Objective-C实例变量?,objective-c,Objective C,我敢肯定,我在这里的困惑只是因为我被困在“Java思维模式”中,不理解Obj-C在这种情况下的区别 在Java中,我可以像这样在类中声明一个变量,该类的每个实例都有自己的变量: MyClass { String myVar; MyClass() { // constructor } } 在Obj-C中,我尝试通过仅在.m文件中声明一个变量来执行相同的操作,如下所示: #import "MyClass.h" @implementation MyClass NSStri

我敢肯定,我在这里的困惑只是因为我被困在“Java思维模式”中,不理解Obj-C在这种情况下的区别

在Java中,我可以像这样在类中声明一个变量,该类的每个实例都有自己的变量:

MyClass {

  String myVar;

  MyClass() {
    // constructor
  }
}
在Obj-C中,我尝试通过仅在.m文件中声明一个变量来执行相同的操作,如下所示:

#import "MyClass.h"

@implementation MyClass

NSString *testVar;

@end
我在这里的期望是这个变量的作用域仅限于这个类。因此,我创建了第二个类(相同):

我所看到的(让我困惑的)是,在一个类中更改变量会影响在另一个类中看到的值。事实上,如果我设置了一个断点,然后“跳转到变量的定义”,它会将我带到

我创建了一个非常小的Xcode项目来演示这个问题。

更改此项:

@implementation MyClass

NSString *testVar;

@end
致:

你会得到你所期望的

正如您所看到的,您实际上正在创建一个全局变量。链接器将这两个全局变量合并为一个,这就是为什么在设置一个全局变量时两者都会发生变化的原因。大括号中的变量将是适当的(私有)实例变量

编辑:在无缘无故被否决后,我想我应该指出“旧”的做事方式和新的做事方式

老办法:

某个班级

@interface SomeClass : UIViewController <UITextFieldDelegate> {
    UITextField *_textField;
    BOOL _someBool;
}

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end
@interface SomeClass : UIViewController

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end
现在,现代Objective-C编译器采用了新的改进方式:

某个班级

@interface SomeClass : UIViewController <UITextFieldDelegate> {
    UITextField *_textField;
    BOOL _someBool;
}

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end
@interface SomeClass : UIViewController

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end
某个班级

@implementation SomeClass

@synthesize someBool = _someBool;

// the method implementations

@end
@interface SomeClass () <UITextFieldDelegate>
@end

@implementation SomeClass {
    UITextField *_textField;
}

// the method implementations

@end
@接口SomeClass()
@结束
@类的实现{
UITextField*_textField;
}
//方法实现
@结束
这种新方法有几个优点。主要的优点是,在.h文件中没有显示有关该类的任何特定于实现的详细信息。客户机不需要知道实现需要什么委托。客户不需要知道我使用什么IVAR。现在,如果实现需要新的ivar或者需要使用新的协议,那么.h文件不会更改。这意味着重新编译的代码更少。它更干净,效率更高。它还使编辑更容易。当我编辑.m文件并意识到我需要一个新的ivar时,在我已经编辑的同一个.m文件中进行更改。不需要来回交换

还要注意,实现不再需要属性的ivar或
@synthesis

在Java中

MyClass {

  String myVar;
  MyClass() {
    // constructor
  }
}
在Objective-c中

MyClass.h

@interface MyClass : NSObject{

      NSString* str; // Declaration
}
@end

MyClass.m

@implementation MyClass

  -(void)initializieTheString
  {
     //Defination 
  }

@end

在objective-c中,您可以这样将变量定义为private

MyClass.h

@interface MyClass : NSObject{

      NSString* _myTestVar; // Declaration
}
@end
并通过这样做在实现类中引用它 我的班级

#import "MyClass.h";

@implementation MyClass

  -(void)initializieTheString
  {
     _myTestVar= @"foo"; //Initialization

  }

@end
您可能想要的(除非您使用的是非常旧的操作系统和编译器)是只使用属性语法。即:

@interface MyClass : NSObject

// method declarations here ...

@property (copy) NSString*    myVar;

// ... or here.

@end
这将完成您打算做的事情。这将隐式地合成一个实例变量和该变量的getter/setter对。如果您想手动创建实例变量(通常不需要该变量,除非您需要您的代码在非常旧的MacOS版本上工作),这就是上面的代码在创建ivar时所做的:

@interface MyClass : NSObject
{
    NSString*    _myVar;
}

// method declarations here.

@end
请注意大括号,它告诉编译器这不仅仅是方法之间的一个全局变量,而是属于此对象的一个实例变量

如果您只为内部使用而创建属性,并且不希望类的客户端弄乱它,那么可以使用类扩展将其隐藏在除最旧的ObjC编译器之外的所有对象中,该类扩展可以“继续”从标头中的类声明,但可以与它分开放置(通常在您的实现文件中)。类扩展名类似于没有名称的类别:

@interface MyClass ()

@property (copy) NSString*    myVar;

@end
您可以将您的属性声明放在其中,甚至可以将ivar声明放在其中(同样用花括号括起来)。您甚至可以在类接口中声明与
readonly
相同的属性,然后在扩展中重新声明与
readwrite
相同的属性,以便客户端仅读取该属性,但您的代码可以对其进行更改

请注意,如果您没有使用ARC(也就是说,您已经关闭了自动引用计数的默认设置),那么您必须在
dealloc
方法中将所有属性设置为
nil
(当然,除非它们设置为
weak
assign

注意-以上都是
@接口
部分。您的实际代码将放在单独的
@实现
部分。这样您就可以有头文件(
.h
)您可以将只包含您希望它们使用的部分的内容传递给类的客户机,并将实现细节隐藏在实现文件(
.m
)中,在那里您可以更改它们,而不必担心有人可能意外使用了它们,并且您会破坏其他代码


PS-请注意,
NSStrings
和其他您想要不可变风格的对象,但也存在于可变风格中(即
NSMutableString
)应该始终是
copy
属性,因为这会将NSMutableString转换为NSString,这样外部的任何人都不能更改您下面的可变字符串。对于所有其他对象类型,您通常使用
strong
(或者
retain
如果不是ARC)。对于类的所有者(例如其委托)通常使用
(或
分配
,如果不是ARC).

Objective-C更像C/C++而不是Java。Java相对独特,它没有单独的声明和实现文件,而是将所有内容放在一个文件中。在Objective-C中,您在.h文件的@interface部分声明实例字段。我猜这是因为您没有描述@interface/@im的规范形式实施。你展示的内容在技术上是允许的,但几乎从未被使用过,可能会让新手产生灾难性的困惑。@HotLicks我展示的是新的首选方法。没有理由再在.h文件中放置私有IVAR。现代LLVM编译器不允许。初学者应该学习新方法,而不是旧方法。@HotLicks我编辑了我的ans回答