好的 - 所以我确定我的困惑只是因为被困在“Java思维模式”而不理解Obj C在这种情况下的不同之处。
在Java中,我可以在类中声明一个变量,就像这样,该类的每个实例都有它自己的:
MyClass {
String myVar;
MyClass() {
// constructor
}
}
在Obj C中,我尝试通过仅在.m文件中声明一个变量来做同样的事情:
#import "MyClass.h"
@implementation MyClass
NSString *testVar;
@end
我的期望是这个变量的范围仅限于这个类。所以我创建了第二个类(相同):
#import "MySecondClass.h"
@implementation MySecondClass
NSString *testVar;
@end
我所看到的(让我感到困惑)是在一个类中更改变量会影响另一个类中看到的值。事实上,如果我设置一个断点,然后“跳转到定义”变量,它就会把我带到
我创建了一个非常小的XCode项目来演示问题here
没有比转向新语言更羞辱了:))
提前致谢。
答案 0 :(得分:125)
改变这个:
@implementation MyClass
NSString *testVar;
@end
为:
@implementation MyClass {
NSString *testVar;
}
// methods go here
@end
你会得到你所期望的。
正如您所拥有的那样,您实际上正在创建一个全局变量。两个全局变量由链接器组合成一个,这就是为什么在设置它们时都会更改的原因。花括号中的变量将是一个适当的(和私有的)实例变量。
编辑:在没有明显理由的情况下被投票后,我想我会指出“旧”的做事方式和新的方式。
旧方式:
SomeClass.h
@interface SomeClass : UIViewController <UITextFieldDelegate> {
UITextField *_textField;
BOOL _someBool;
}
@property (nonatomic, assign) BOOL someBool;
// a few method declarations
@end
SomeClass.m
@implementation SomeClass
@synthesize someBool = _someBool;
// the method implementations
@end
现在使用现代Objective-C编译器的新方法和改进方法:
SomeClass.h
@interface SomeClass : UIViewController
@property (nonatomic, assign) BOOL someBool;
// a few method declarations
@end
SomeClass.m
@interface SomeClass () <UITextFieldDelegate>
@end
@implementation SomeClass {
UITextField *_textField;
}
// the method implementations
@end
新方式有几个优点。主要优点是没有关于该类的实现特定细节出现在.h文件中。客户端无需知道代表实现需要的内容。客户无需知道我使用的是哪种ivars。现在,如果实现需要新的ivar或者需要使用新协议,则.h文件不会更改。这意味着更少的代码被重新编译。它更清洁,更有效率。它还使编辑更容易。当我正在编辑.m文件并意识到我需要一个新的ivar时,在我正在编辑的同一个.m文件中进行更改。无需来回交换。
另请注意,该实施不再需要ivar或@synthesize
。
答案 1 :(得分:1)
在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
答案 2 :(得分:1)
您可能想要的(除非您使用的是非常旧的操作系统和编译器)只是使用属性语法。即:
@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编译器之外,您可以使用隐藏这一点。类扩展,&#34;继续&#34;标题中的类声明,但可以与它分开放置(通常在您的实现文件中)。类扩展看起来像没有名称的类别:
@interface MyClass ()
@property (copy) NSString* myVar;
@end
你可以把你的财产声明放在那里,甚至是ivar声明(再次用大括号括起来)。您甚至可以在类接口中声明与readonly
相同的属性,然后重新声明它相同,但在扩展名中为readwrite
,以便客户端只读取它,但您的代码可以更改它
请注意,如果您没有使用ARC(也就是说,您已经关闭了默认的自动引用计数),则必须将所有属性设置为nil
dealloc
方法(除非他们当然设置为weak
或assign
)。
注意 - 以上所有都是@interface
部分。您的实际代码将放在单独的@implementation
部分中。这样您就可以将头文件(.h
)移交给您的类客户端,这些客户端只包含您打算使用它们的部分,并隐藏实现文件中的实现细节({{ 1}})你可以改变它们而不必担心有人可能会意外地使用它们而你会破坏其他代码。
PS - 请注意.m
和其他您想要不可变味道但又存在于可变味道中的对象(即NSStrings
)应始终为NSMutableString
属性,因为这会将NSMutableString转换为NSString,这样外面的任何人都无法更改你下面的可变字符串。对于所有其他对象类型,通常使用copy
(如果不是ARC,则为strong
)。对于您班级的所有者(例如其代理人),您通常使用retain
(如果不是ARC,则为weak
。
答案 3 :(得分:0)
在objective-c中,您可以通过这样做将变量定义为私有
MyClass.h
@interface MyClass : NSObject{
NSString* _myTestVar; // Declaration
}
@end
并通过这样做在实现类中引用它 MyClass.m
#import "MyClass.h";
@implementation MyClass
-(void)initializieTheString
{
_myTestVar= @"foo"; //Initialization
}
@end