5

我编码的越多,我就越迷失......所以我决定为我(和其他人)创建一个完全致力于内存管理的主题,而不是浪费时间理解 obj-c 基础......我将它更新为提出了新的问题!

好的,下面是一些示例:

// myArray is property (retain)
myArray = otherArray;

//myArray isn't a property
myArray = otherArray;

//myArray is a property (retain)
myArray = [[NSArray alloc] init];

//myArray isn't a property
myArray = [[NSArray alloc] init];

--- 所以,如果我理解……当你放 self.myArray 时,你告诉 Xcode 使用 getter 或 setter,但是当你只做 myArray 时,你负责一切,对吧?

[已解决] UPDATE1:两者之间是否有区别:

//myArray is a property
myArray = otherArray; // it is only a reference, releasing otherArray will imply releasing myArray
self.myArray = otherArray; // otherArray is sent a retain message so releasing otherArray will still keep myArray in memory

--- 是的,有区别(见上面的评论)

[已解决] UPDATE2:下面的 myArray 是否等于 nil ?

NSArray *myArray;

--- Kubi : 是的,它等于 nil。

[已解决] UPDATE3:它是否计入 2 次保留?一个从 self 中保留,一个从 alloc 中保留?这是内存泄漏吗?

self.myArray = [[NSArray alloc] init];

--- Kubi:是的,这是内存泄漏!

[已解决] UPDATE4:物业负责一切?不需要分配或释放?

self.myArray = [NSArray array];

--- 我们这里使用 setter 以便正确保留数组

[已解决] UPDATE5:这两个块相同吗?

//myArray is a retained property

self.myArray = [NSArray array]; //retain
self.myArray = nil; //release and set to nil

myArray = [[NSArray alloc] initWithArray]; //retain
self.myArray = nil; //release and set to nil

--- Kubi : 是的,它们是一样的

谢谢你的时间。

哥提。

4

3 回答 3

6

首先,我假设您有一个名为的属性myArray和一个名为的 iVar myArray?如果是这样,则案例 1,2 相同,案例 3,4 相同。如果您需要设置当前类的属性,则必须通过以下方法之一进行:

self.myArray = otherArray;
[self setMyArray:otherArray];

该行myArray = otherArray只会设置 iVar,而不是属性。

第二部分,你问的是内存管理。第一步:阅读Apple 的指南。真的是必读。不完全理解也不要着急,坚持一个月读一次,终会明朗。

第二步:记住这条经验法则:如果你alloc, copy, new, 或retain一个对象,你负责释放那个对象,如果你不这样做,它就会被泄露。

在所有其他情况下,您不负责释放对象,但它最终会被释放。如果您需要保留它,则需要retain它(当然,稍后再释放它)。

回到您的示例,在前两种情况下,如果您不保留myArray它将在此代码块之后的某个时间点发布。如果您稍后尝试向该对象发送消息,则会收到错误消息。在后两种情况下,如果您在某些时候不释放myArray对象,它将被泄漏。


更新 1 差别很大。两条线完全不同。关于点语法要意识到的重要一点是这两行是完全等价的:

self.myArray = otherArray;
[self setMyArray:otherArray];

注意第二行是一个方法调用。从理论上讲,您可以在该方法中放入任何您想要的东西。您可以设置myArray为 nil,或将其设置为someOtherArray,或更新 twitter 或其他任何内容。


更新 2 是的,Obj-C 中的指针被初始化为 nil。


更新 3 正是如此。如果该myArray属性被声明为retain并且您使用的是默认合成器,您将导致内存泄漏。

更新 5 也完全正确。

于 2010-03-22T23:34:12.753 回答
5

库比的回答很好。重读 Apple 的指南,直到你真正理解它是必须的。

同时,您可能会受益于采用我遵循的这套严格的做法以避免意外的内存错误。这些规定比严格必要的类型多一点,并且它们在运行时的效率可能比它们可能的稍低,但始终遵循这些规则将保护您免受最常见的内存管理错误的影响。然后,随着您对内存管理越来越熟悉,您可能会选择有选择地偏离这些规则,尽管我仍然很少这样做。

  1. 为您创建的每个对象实例变量声明一个属性。将其声明为(非原子,保留),除非它是您的类不拥有的对象,例如委托,这将创建循环引用。在这种情况下,将其声明为 (nonatomic, assign) 以避免泄漏该循环中的所有对象。

    @property (nonatomic, retain) NSString *title;
    @property (nonatomic, assign) id <WidgetDelegate> delegate;
    

  2. 即使对象实例变量仅供类私有使用,也要在 .m 文件顶部的类扩展中为其声明一个属性,以便合成的 setter 方法可以为您处理内存管理。

    // Widget.m
    @interface Widget()
    @property (nonatomic, retain) NSString *privateState;
    @end
    
    @implementation Widget
    @synthesize title, delegate, privateState;
    // ...
    @end
    
  3. 任何时候你分配一个实例变量对象,总是通过使用 self.properties 的属性来设置它。

    self.title = @"Title";

  4. 在您的 dealloc 中,将每个对象属性设置为 nil。如果您遵循上述做法,这将同时正确释放您的实例变量并将它们设置为 nil 以防止 EXC_BAD_ACCESS。使 dealloc 成为您类中的第一个方法,这样您就不会忘记任何属性。

    - (void) dealloc {
        self.title = nil;
        self.delegate = nil;
        self.privateState = nil;
        [super dealloc];
    }
    
  5. 对于您编写的每个自定义类,请确保它至少具有一个类工厂方法,该方法委托给具有相同参数的 init 方法并自动释放返回的对象。这限制了对这些工厂方法的几乎所有 alloc 和 init 调用,而不是将它们分散在整个代码中。

    - (id)initWithTitle:(NSString *)theTitle delegate:(id )theDelegate {
        if (self = [super init]) {
            self.title = theTitle;
            self.delegate = theDelegate;
            self.privateState = @"start";
        }
        return self;
    }
    + (id)widgetWithTitle:(NSString *)theTitle delegate:(id )theDelegate {
        return [[[self alloc] initWithTitle:theTitle delegate:theDelegate] autorelease];
    }
    
  6. 每当您实例化一个对象时,如果可能,请始终通过工厂类方法进行。这为您提供了一个自动释放的对象,因此除非您保留它,否则您不必释放它。

    self.widget = [Widget widgetWithTitle:@"My Widget" delegate:self];
    
  7. 当你需要实例化一个没有合适的工厂类方法的对象时,在同一行自动释放它,这样你以后不要忘记做。(例外:如果您在紧密循环中执行此操作数千次,请手动释放。)

    self.containerView = [[[UIView alloc] initWithFrame:self.bounds] autorelease]; 
    
  8. 如果您要释放的对象具有指向循环引用的委托或类似属性,请先将该属性设置为 nil。这可以防止 EXC_BAD_ACCESS 对象超过其委托并在其被释放后尝试调用委托上的方法。

    - (void)dealloc {
        self.widget.delegate = nil;
        self.widget = nil;
        self.containerView = nil;
        [super dealloc];
    }
    

许多有经验的开发人员在没有遵循所有这些做法的情况下成功地管理内存。如果您了解内存管理并且不倾向于遇到与内存相关的错误,那么我当然鼓励您坚持使用适合您的任何方法。但是,如果您是 iPhone 内存管理的新手,或者您的代码受到与内存相关的意外错误的困扰,我希望这些做法对您有帮助。

于 2010-03-23T11:28:07.080 回答
0

找到答案的最佳位置是在解释所有有关内存管理的相关 Apple 文档中。话虽如此,您只是在这里使用 ivars,而不是使用 ObjC 生成的 setter 设置您的 ivar。正如 kubi 所说,你不得不说

self.myArray = otherArray;

为了使用 myArray 的“属性”。

于 2010-03-22T23:38:43.333 回答