Objective C ivars或@property

时间:2011-08-04 13:37:12

标签: iphone objective-c ios

在iPhone上工作之后,经过很多头痛和内存问题后我才从其他示例中意识到,我们不需要为头文件中定义的每个实例变量创建@properties。实际上我发现ivars在我在课堂上的任何地方使用之后都很容易分配和释放它,对于@properties我必须使用自动翻译或者我有严重的问题并且因为我分配了...

例如,对于下面的对象,许多示例中的标题中未使用@properties(retain / copy ..);

{
NSURLConnection *connection;
NSMutableData *xmlData;
NsMutableString *string
} 

但是对于某些字符串或对象类型使用@properties,我知道当我们设置@property时,cocoa会创建一些setter getter来处理对象的重新发送和保留。但似乎对于xmlData或连接实例变量我们不需要这样做,他们就像这样做。

在决定是否创建@ property或仅使用简单的ivars时,是否有一些参考指南?

使用属性时我唯一的问题是因为我懒得定义它,但是当我在代码中仔细分配和初始化它们时,我必须使用autorelase并且不觉得我有控件何时释放重置并分配它再次,它给了我一个额外的事情,担心我何时以及何时应该释放,重置它。我发现ivars我可以随时轻松地分配和释放一次而不用担心任何事情......或者我在这里错过了其他的东西。

TNX

4 个答案:

答案 0 :(得分:6)

似乎仍然存在一些关于房产的错误观念。

  

我们不需要为我们在头文件中定义的每个实例变量创建@properties

正确。您可以直接在实现文件中使用私有实例变量。但是,由于合成属性带有可用内存管理,因此您也可以利用它。我的经验法则是直接使用ivar,直到我第一次发现自己写作:

[ivar release];
ivar = [newIvar retain];

正如Sam所说,如果iVar == newIVar已经存在潜在的错误。这是我从使用ivars直接切换到创建属性的关键点。但是,我将新属性的声明放在实现文件中的class extension中。这意味着该属性正式不属于公共接口(如果意外使用,将导致编译器警告)。

  

当我们设置@property时,cocoa会创建一些setter getter来处理对象的重新发送和保留。

实际上,没有。 @property只声明了一个属性。为了自动生成getter和setter,你需要@synthesize它。你也可以编写自己的getter和setter,甚至不需要引用真正的ivar。

从技术上讲,你不应该在init或dealloc方法中使用该属性,因为子类可能已经覆盖了它们,或者(在dealloc中)你可能会设置KVO通知。


来自Sam的回答和评论

  

如果您想要一个属性,可以在实现文件的顶部使用私有接口

正如我上面所说的那样,私有类别已经被类扩展废弃了(它几乎相同,但允许你将方法的实现放在主类实现中)。

  

如果您想要使用点符号简写的好处

我们中的一些人会争辩说点符号没有任何好处。这是结构成员语法的无偿和不必要的污染。但是,点符号与@property声明无关。您可以对任何访问者使用点表示法,无论它们如何声明,只要它们符合模式-foo-setFoo:

答案 1 :(得分:4)

仅为需要从类外部访问的变量创建属性。内部使用的任何类变量都不需要定义getter / setter。

一般而言,丰富的性质表明高耦合和差的封装。您应该限制您的类在界面中公开的变量。

编辑回复评论:

使用属性而不是直接访问可能是首选,因为它可以让您轻松进行内存管理..例如:

// interface
@property (retain) Object *someVar;     

// implementation
self.someVar = otherVar;

相同
// implementation
if (_someVar != othervar)
{
    [_someVar release]
    _someVar = [otherVar retain];
}

但是,你不应该在你的界面中不必要地暴露vars,因为它打开了课程,供人们以错误的方式使用。

如果您想要一个属性,可以在实现文件的顶部使用私有接口

@interface TheClass(Private)
    // private stuff
@end

答案 2 :(得分:1)

长期以来,人们一直习惯直接访问ivars。也就是说,IMO,在同一个类中很好,尽管许多属性是类,然后属性提供保护以防止保留/释放问题。

然而,IMO,最好将大多数ivars封装到属性中,特别是那些具有保留/释放语义的属性,以及那些需要特殊处理的属性,即您编写自己的处理程序,而不是使用合成的处理程序。 。这样,您可以过滤对某些ivars的访问,甚至创建没有任何后备存储的属性,并且只是其他属性的“别名”,例如一个Angle类,它有一个degrees属性给出以度为单位的角度,一个radians属性用弧度表示相同的角度(这是一个简单的转换),或者一个必须进行字典搜索才能找到它的值的属性等。

在Delphi中,(AFAICT)是第一批具有属性作为语言构造的语言之一,习惯上将所有ivars包装在属性中(但并非所有都必须公开),并且有许多这样的“不真实” “(我故意避免使用”虚拟“这一术语)属性,即仅在代码中实现的属性,而不仅仅是用于ivar的getter和setter。

属性提供封装抽象以及针对某些经常出现的错误的保护程度,这就是为什么他们会被首选直接访问伊利诺伊州,IMO。

加成

声明和实施(通过@synthesize或自定义设置器和getter)每个 ivar的公共属性是没有意义的。只公开其他人可能需要的东西。内部状态也可以通过属性暴露给您自己的代码,但应该在实现文件中使用私有或空类别。这样,您就可以自动处理保留/释放,但仍然不会将其暴露给公众。

答案 3 :(得分:1)

首先,让我说Sam的答案是完整的,IMO,并为您提供明确的指导方针(+1来自我)。

  

使用属性时我唯一的问题是因为我懒得定义它,但是当我在代码中仔细分配和初始化它们时,我必须使用autorelase并且不觉得我有控件何时释放重置并分配它再次,它给了我一个额外的事情,担心我何时以及何时应该释放,重置它。我发现ivars我可以随时轻松地分配和释放一次而不用担心任何事情......或者我在这里错过了其他的东西。

您不应该担心以下习语中的autorelease

self.stringProperty = [[[NSString alloc] initWith...] autorelease];

因为这是事物的工作方式;

编辑:[上述声明有几个部分:

  1. 对象已分配并初始化(保留计数为1);

  2. 立即
  3. ,分配的对象也是autoreleased;这意味着当控制流回到主循环时,对象将自动释放(或多或少);

  4. 在同一语句中,已分配的对象被分配给保留属性self.stringProperty;这具有(再次)增加保留计数的效果;

  5. 因此,autorelease确实增加了一些歧义",因为该对象将在您不确切知道的时间发布(但很快就会知道),但是,分配到retain属性会增加保留计数,以便对对象的生命周期进行完全控制。]

    如果您不喜欢autorelease,您可以随时使用构造函数方法,该方法可以返回自动释放的对象(如果可用):

    self.stringProperty = [NSString stringWith...];
    

    或直接分配到ivar:

    stringProperty = [[[NSString alloc] initWith...] autorelease];
    

    因为通过直接访问ivar,你绕过了setter和getter。无论如何,在这种情况下(IMHO)只执行 以避免含糊不清。

    更一般地说,autorelease故障是使用属性直接访问ivars的唯一缺点。其余的,IMO,只有在许多情况下会挽救你的生命,如果不是你的生命,泄漏或崩溃的优势。

    直接访问ivars并在分配之前需要释放时,或者在释放后不要忘记设置为nil等等,没有什么是你无法做到的,但属性对你来说更容易,所以我的建议只是使用它们并接受autorelease的缺点。只需要获得基本的成语"右。