Ale*_*lin 111 iphone objective-c ios ios5
自从开始研究iOS应用程序和目标C以来,我一直对可以声明和定义变量的不同位置感到困惑.一方面我们采用传统的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
Run Code Online (Sandbox Code Playgroud)
和
#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
Run Code Online (Sandbox Code Playgroud)
谢谢大家,伙计们!
Dru*_*erB 151
我能理解你的困惑.特别是因为最近对Xcode和新LLVM编译器的更新改变了ivars和属性的声明方式.
在"现代"Objective-C(在"旧"Obj-C 2.0中)之前,你没有太多选择.实例变量曾在大括号之间的标头中声明{ }:
// MyClass.h
@interface MyClass : NSObject {
int myVar;
}
@end
Run Code Online (Sandbox Code Playgroud)
您只能在实现中访问这些变量,而不能从其他类访问这些变量.要做到这一点,你必须声明访问器方法,看起来像这样:
// 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
Run Code Online (Sandbox Code Playgroud)
这样你就可以从其他类中获取和设置这个实例变量,使用通常的方括号语法来发送消息(调用方法):
// OtherClass.m
int v = [myClass myVar]; // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];
Run Code Online (Sandbox Code Playgroud)
因为手动声明和实现每个访问器方法非常烦人,@property并且@synthesize被引入以自动生成访问器方法:
// MyClass.h
@interface MyClass : NSObject {
int myVar;
}
@property (nonatomic) int myVar;
@end
// MyClass.m
@implementation MyClass
@synthesize myVar;
@end
Run Code Online (Sandbox Code Playgroud)
结果是更清晰和更短的代码.将为您实现访问器方法,您仍然可以像以前一样使用括号语法.但此外,您还可以使用点语法来访问属性:
// OtherClass.m
int v = myClass.myVar; // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;
Run Code Online (Sandbox Code Playgroud)
从Xcode 4.4开始,您不必再自己声明实例变量,也可以跳过@synthesize.如果你没有声明一个ivar,编译器会为你添加它,它也会生成访问器方法而不必使用@synthesize.
自动生成的ivar的默认名称是以下划线开头的名称或属性.您可以使用更改生成的ivar名称@synthesize myVar = iVarName;
// MyClass.h
@interface MyClass : NSObject
@property (nonatomic) int myVar;
@end
// MyClass.m
@implementation MyClass
@end
Run Code Online (Sandbox Code Playgroud)
这将完全像上面的代码一样工作.出于兼容性原因,您仍然可以在标头中声明ivars.但是因为您想要这样做(而不是声明属性)的唯一原因是创建一个私有变量,您现在也可以在实现文件中执行此操作,这是首选方法.
@interface实现文件中的块实际上是一个扩展,可用于转发声明方法(不再需要)和(重新)声明属性.例如,您可以readonly在标头中声明一个属性.
@property (nonatomic, readonly) myReadOnlyVar;
Run Code Online (Sandbox Code Playgroud)
并在您的实现文件中重新声明它,readwrite以便能够使用属性语法设置它,而不仅仅是通过直接访问ivar.
至于完全在任何@interface或@implementation块之外声明变量,是的那些是纯C变量并且工作完全相同.
Rob*_*ier 42
首先,阅读@ DrummerB的回答.它很好地概述了为什么以及您通常应该做什么.考虑到这一点,针对您的具体问题:
#import <Foundation/Foundation.h>
// 1) What do I declare here?
Run Code Online (Sandbox Code Playgroud)
这里没有实际的变量定义(如果您确切地知道自己在做什么,那么在技术上是合法的,但从不这样做).您可以定义其他几种东西:
Externs看起来像变量声明,但它们只是在其他地方实际声明它的承诺.在ObjC中,它们只应用于声明常量,通常只用于字符串常量.例如:
extern NSString * const MYSomethingHappenedNotification;
Run Code Online (Sandbox Code Playgroud)
然后,您将在您的.m文件中声明实际常量:
NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";
Run Code Online (Sandbox Code Playgroud)
@interface SampleClass : NSObject
{
// 2) ivar declarations
// Pretty much never used?
}
Run Code Online (Sandbox Code Playgroud)
正如DrummerB所说,这是遗产.不要在这里放任何东西.
// 3) class-specific method / property declarations
@end
Run Code Online (Sandbox Code Playgroud)
是的.
#import "SampleClass.h"
// 4) what goes here?
Run Code Online (Sandbox Code Playgroud)
外部常数,如上所述.文件静态变量也可以在这里.这些等同于其他语言中的类变量.
@interface SampleClass()
// 5) private interface, can define private methods and properties here
@end
Run Code Online (Sandbox Code Playgroud)
是的
@implementation SampleClass
{
// 6) define ivars
}
Run Code Online (Sandbox Code Playgroud)
但很少.几乎总是你应该允许clang(Xcode)为你创建变量.例外情况通常围绕非ObjC ivars(如Core Foundation对象,尤其是C++对象,如果这是一个ObjC++类),或者是具有奇怪存储语义的ivars(例如由于某些原因与属性不匹配的ivars).
// 7) define methods and synthesize properties from both public and private
// interfaces
Run Code Online (Sandbox Code Playgroud)
一般来说你不应该@synthesize了.Clang(Xcode)会为你做,你应该让它.
在过去的几年里,事情变得非常简单.副作用是现在有三个不同的时代(脆弱的ABI,非脆弱的ABI,非脆弱的ABI +自动合成).因此,当您看到较旧的代码时,可能会有点混乱.因此,简单性引起的混乱:D
我也很新,所以希望我不要搞砸任何东西.
1和4:C风格的全局变量:它们具有文件范围.两者之间的区别在于,由于它们是文件范围的,因此第一个将可用于导入标题的任何人,而第二个则不可用.
2:实例变量.大多数实例变量是使用属性通过访问器进行合成和检索/设置的,因为它使内存管理变得简单,并且为您提供易于理解的点表示法.
6:实施ivars有点新.这是放置私有ivars的好地方,因为你只想暴露公共标题中需要的内容,但是子类不会继承它们AFAIK.
3&7:公共方法和属性声明,然后是实现.
5:专用接口.每当我能保持干净并创造一种黑盒效果时,我总是使用私有接口.如果他们不需要知道它,就把它放在那里.我也这样做是为了可读性,不知道是否还有其他原因.
这是Objective-C中声明的各种变量的示例.变量名称表示其访问权限.
文件: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
Run Code Online (Sandbox Code Playgroud)
文件: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
Run Code Online (Sandbox Code Playgroud)
请注意,iNotVisible变量在任何其他类中都不可见.这是一个可见性问题,因此声明@property或@public不更改它们.
在构造函数中,最好@property使用下划线来访问声明的变量,self以避免副作用.
让我们尝试访问变量.
文件:Cow.h
#import "Animal.h"
@interface Cow : Animal
@end
Run Code Online (Sandbox Code Playgroud)
文件: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
Run Code Online (Sandbox Code Playgroud)
我们仍然可以使用运行时访问不可见的变量.
File: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
Run Code Online (Sandbox Code Playgroud)
让我们尝试访问不可见的变量.
文件:main.m
#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
@autoreleasepool {
Cow *cow = [Cow new];
[cow performSelector:@selector(blindAccess)];
}
}
Run Code Online (Sandbox Code Playgroud)
这打印
iMadeVisible
iMadeVisible2
iMadeVisible3
Run Code Online (Sandbox Code Playgroud)
请注意,我能够访问_iNotVisible2子类私有的支持ivar .在Objective-C中,所有变量都可以被读取或设置,甚至是那些被标记的变量@private,没有例外.
我没有包含相关对象或C变量,因为它们是不同的鸟类.对于C变量,任何在外部定义的变量@interface X{}或者@implementation X{}是具有文件范围和静态存储的C变量.
我没有讨论内存管理属性,或readonly/readwrite,getter/setter属性.
| 归档时间: |
|
| 查看次数: |
49532 次 |
| 最近记录: |