保留、分配、属性...让您的 Obj-c 生活更轻松的主题!

发布于 2024-08-26 12:11:16 字数 1711 浏览 10 评论 0原文

我编写的代码越多,迷失的就越多......所以我决定为我(和其他人)创建一个完全致力于内存管理的主题,而不是浪费时间来理解 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:是的,它们是相同的

感谢您抽出时间。

戈蒂耶。

The more I code, the more I get lost ... so I decided to create a topic entirely dedicated to the memory management for me (and others) not to waste hours understanding obj-c basics ... I'll update it as new questions are asked !

Okay below is some examples :

// 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];

--- So, if I understand ... when you put self.myArray you tell Xcode to use the getter or setter but when you just do myArray, you are responsible for everything, right ?

[SOLVED] UPDATE1 : Is there a difference between :

//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

--- Yes, there is a difference (see comments above)

[SOLVED] UPDATE2 : Is myArray below equal to nil ?

NSArray *myArray;

--- Kubi : Yes, it is equal to nil.

[SOLVED] UPDATE3 : does it count for 2 retains ? One retain from self and one retain from alloc ? Is this a memory leak ?

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

--- Kubi : Yes, this is a memory leak !

[SOLVED] UPDATE4 : the property takes care of everything ? no need to alloc or release ?

self.myArray = [NSArray array];

--- We here use the setter so that the array is retained properly

[SOLVED] UPDATE5 : Are these two blocks the same ?

//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 : Yes, they're identical

Thanks for the time.

Gotye.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

活雷疯 2024-09-02 12:11:16

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

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

myArray = otherArray 行将仅设置 iVar,而不会设置属性。

第二部分,您询问内存管理。第一步:阅读 Apple 指南。这确实是必读的。如果您不完全理解,请不要担心,每月继续阅读一次,它最终会变得清晰。

第二步:记住这条经验法则:如果您分配复制新建保留一个对象,你有责任释放该对象,如果你不这样做,它就会被泄漏。

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

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


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

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

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


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


更新3
确切地。如果 myArray 属性声明为 retain 并且您使用默认合成器,则会导致内存泄漏。

更新5
也完全正确。

First, I'm assuming that you have a property called myArray and an iVar called myArray? If so, cases 1,2 are identical and 3,4 are identical. If you need to set a property of your current class, you must do it through one of the following methods:

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

the line myArray = otherArray will only set the iVar, never the property.

Second part, you're asking about memory management. Step one: read Apple's Guide. It really is required reading. Don't worry if you don't completely understanding, keep reading it once a month and it'll crystalize eventually.

Step two: remember this rule of thumb: If you alloc, copy, new, or retain an object, you are responsible for releasing that object, if you don't, it will be leaked.

In all other cases, you are not responsible for releasing the object, but it will be released eventually. If you need to keep it around, you need to retain it (and, of course, release it later).

Back to your example, in the first two cases, if you don't retain myArray it'll be released at some point after this code block. If you try and message that object later, you'll get an error. In the second two cases, if you don't release the myArray object at some point, it will be leaked.


Update 1
Very big difference. The two lines are completely different. The important thing to realize about dot syntax is that these two lines are exactly equivalent:

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

Notice the second line is a method call. Theoretically you could put anything you wanted in that method. You could set myArray to nil, or set it to someOtherArray, or update twitter or whatever.


Update 2
Yup, pointers in Obj-C are initialized to nil.


Update 3
Exactly. If the myArray property is declared as retain and you're using the default synthesizers you will be causing a memory leak.

Update 5
Also exactly right.

看透却不说透 2024-09-02 12:11:16

库比的回答很好。重读苹果指南直到你真正理解它是非常必要的。

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

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

    @property (非原子,保留) NSString *title;
    @property(非原子,分配)id 代表;
    

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

    // Widget.m
    @界面小部件()
    @property(非原子,保留)NSString *privateState;
    @结尾
    
    @实现小部件
    @synthesize 标题、委托、privateState;
    // ...
    @结尾
    
  3. 任何时候分配实例变量对象时,始终使用 self 通过属性来设置它。

    self.title = @"Title";

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

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

    - (id)initWithTitle:(NSString *)theTitle 委托:(id )theDelegate {
        if (self = [超级初始化]) {
            self.title = theTitle;
            self.delegate = theDelegate;
            self.privateState = @"开始";
        }
        返回自我;
    }
    + (id)widgetWithTitle:(NSString *)theTitle 委托:(id )theDelegate {
        return [[[self alloc] initWithTitle:theTitle delegate:theDelegate] autorelease];
    }
    
  6. 每当实例化一个对象时,如果可能的话,请始终通过工厂类方法来完成。这为您提供了一个自动释放的对象,因此您不必释放它,除非您保留它。

    self.widget = [Widget widgetWithTitle:@"我的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;
        [超级释放];
    }
    

许多经验丰富的开发人员在没有遵循所有这些实践的情况下成功地管理了内存。如果您了解内存管理并且不会出现与内存相关的错误,我当然会鼓励您坚持使用适合您的方法。但是,如果您是 iPhone 内存管理的新手,或者您的代码受到与内存相关的意外错误的困扰,我希望您发现这些实践和我一样有帮助。

Kubi's answer is good. Rereading Apple's Guide until you grok it is really mandatory.

In the mean time, you may benefit by adopting this strict set of practices I follow to avoid accidental memory errors. These prescribe a little more typing than is strictly necessary, and they're probably slightly less efficient at runtime than they could be, but following these rules consistently will protect you against the most common memory management errors. Then as you become more comfortable with memory management, you may choose to selectively deviate from these rules, although I still rarely do.

  1. Declare a property for every object instance variable you create. Declare it as (nonatomic, retain), unless it's an object your class doesn't own, like a delegate, which would create a circular reference. In that case, declare it as (nonatomic, assign) to avoid leaking all the objects in that cycle.

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

  2. Even if an object instance variable is intended only for private use by the class, declare a property for it in a class extension at the top of your .m file so that the synthesized setter method can take care of memory management for you.

    // Widget.m
    @interface Widget()
    @property (nonatomic, retain) NSString *privateState;
    @end
    
    @implementation Widget
    @synthesize title, delegate, privateState;
    // ...
    @end
    
  3. Any time you assign an instance variable object, always set it through the property using self.

    self.title = @"Title";

  4. In your dealloc, set every object property to nil. If you've followed the above practices, this will simultaneously release your instance variables properly and set them to nil to guard against EXC_BAD_ACCESS. Make dealloc the first method in your class so you don't forget any properties.

    - (void) dealloc {
        self.title = nil;
        self.delegate = nil;
        self.privateState = nil;
        [super dealloc];
    }
    
  5. For every custom class you write, make sure it has at least one class factory method that delegates to an init method with the same parameters and autoreleases the returned object. This confines almost all alloc and init calls to these factory methods rather than scattering them throughout your code.

    - (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. Whenever you instantiate an object, always do it through a factory class method if possible. This gives you an autoreleased object, so you don't have to release it unless you retain it.

    self.widget = [Widget widgetWithTitle:@"My Widget" delegate:self];
    
  7. When you need to instantiate an object that has no appropriate factory class method, autorelease it on the same line, so you don't forget to do it later. (Exception: release manually if you're doing this thousands of times in a tight loop.)

    self.containerView = [[[UIView alloc] initWithFrame:self.bounds] autorelease]; 
    
  8. If you're releasing an object that has a delegate or similar property that points back in a circular reference, set that property to nil first. This prevents EXC_BAD_ACCESS in the event that an object outlives its delegate and tries to call a method on the delegate after it's been dealloced.

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

Many experienced developers manage memory successfully without following all of these practices. If you understand memory management and don't tend to have memory related bugs, I'd certainly encourage you to stick with whatever works for you. But if you're new to iPhone memory management, or if your code is plagued with accidental memory related bugs, I hope you find these practices as helpful as I have.

Smile简单爱 2024-09-02 12:11:16

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

self.myArray = otherArray;

才能使用 myArray 的“属性”。

The best place to find an answer for this would be in the relevant Apple documentation that explains all about memory management. Having said that, you're only using the ivars here, you're not setting your ivar using the ObjC generated setters. As kubi says, you'd have to say

self.myArray = otherArray;

in order to use myArray's "property"ness.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文