How does the NSWindow isReleasedWhenClosed property work when using ARC?

In Objective-C with ARC or Swift (which always uses ARC), what difference does it make whether isReleasedWhenClosed is true or not? Won’t a window be kept alive as long as my code has strong references to it, and automatically destroyed when the last strong reference is gone? So why is this setting needed at all?

The isReleasedWhenClosed property has been a major source of confusion for decades. Even in modern Swift code, people misunderstand this property and its purpose, or how it may or may not affect their code and application behavior. To understand why isReleasedWhenClosed even exists, we have to go back to a time when Swift didn’t exist and Objective-C had no automatic memory management or garbage collection, it had no weak references and it didn’t even have synthesized properties (@propertry), you had to write getter/setter methods yourself.

Before ARC, you had to manage memory yourself in Obj-C. That is, if you created an object using a method starting with new... or alloc..., you had to release that object when you were done with it by calling release on it. Now, if you passed that object around and someone else wanted to keep a persistent reference to it, they had to retain the object, which would prevent it from being destroyed when its creator called release. But if you retained an object, you also had to offset that retention with another release, otherwise the object would never be destroyed.

What happens if you keep a reference to an object without holding it? In this case, the object may be destroyed and your reference becomes a dangling pointer. A dangling pointer is a pointer to an address where an object used to be, but that object no longer exists, so there is either nothing or another object at that address, or the address points to the middle of some memory allocation. There’s nothing wrong with having dangling object pointers, they’re perfectly legal as long as you don’t actually try to send them messages, because that will result in undefined behavior, meaning maybe it will cause your application to crash, maybe it will cause your application to behave in weird ways, just don’t do it.

So the rules for proper memory management were basically:

  • You release any object you created.
  • You keep any object you want to keep.
  • You release any object you have retained.
  • Assigning an object reference to a variable has no effect on the lifetime of the object.
  • When your object is destroyed, be sure to deallocate any created/retained objects in instance variables.

In modern Obj-C, this code is safe

@implementation SomeObject
    {
        id _ivar;
    }


    - (void)lookAtMyCoolObject:(id)object
    {
        _ivar = object;
    }
@end

as object gets retained the moment you assign it to _ivar but in old school Obj-C that would be fatal. Instead you would have to write

_ivar = [object retain];

and to prevent the object from leaking, you had to add

- (void)dealloc
{
    [_ivar release];
    [super dealloc];
}

You can get the same behavior in modern Obj-C by declaring the variable as __unsafe_unretain. For synthesized properties you would use assign and in Swift you would use unowned. In general, however, there is no need for this construct anymore, as weak references now exist and you would just declare variables as __weak or use weak for properties and in Swift. The advantage of weak references is that they never become dangling pointers. When the object they reference is released, they become nil instead, and sending messages to nil is defined behavior.

Now suppose you had a NIB file describing an NSWindow containing just a text label, named "MyWindow.nib", and you wanted to load that window into your application, how would you have done that in old school Obj-C code?

First, you would have created a class to manage that window. You could subclass NSWindowController, but you didn’t have to, you could just as well do this

@interface MyWindowController : NSObject
// ...
@end


@implementation MyWindowController
    {
        IBOutlet NSWindow * _window;
        IBOutlet NSTextField * _label;
    }


    - (instancetype)init 
    {
        self = [super init];
        if (self) {
            [[NSBundle mainBundle] 
                loadNibNamed:@"MyWindow" owner:self topLevelObjects:nil
            ];
        }
        return self;
    }
@end

In the NIB file, set the MyWindowController class as the owner of the NIB file, and then use drag’n drop in the Interface Builder to assign the window to _window and the label to _label. Done.

But wait, how does memory management work here? There are no retain/release calls anywhere in the code, and both instance variables do not retain anything just because a value is assigned to them. There is also no dealloc that would release anything.

To make memory management for the UI easier, Apple decided that windows retain themselves on load, and a window retains everything found within the window. So MyWindowController in fact retains nothing in the above code. It has an unsafe reference to the window, which stays alive because it has self-retained, and another unsafe reference to the label inside the window, which stays alive because the window has retained it.

What about freeing these objects? The code above is fine if the window exists as long as the application is running, because then there is no need to ever free anything, because when the application quits, all its resources are implicitly freed by the system anyway. However, sometimes an application has multiple windows that come and go, and then you also want to free the memory used by these UI objects at some point. And now we finally come to isReleasedWhenClosed!

If set to true, the moment the window is closed, it will release itself once to balance the self-retention on load. This causes the window to be destroyed, which causes the label to be released and destroyed as well. As a result, both instance variables above are now dangling pointers and cannot be used anymore. So typically you would want to make sure that when the window is closed, MyWindowController is also destroyed, or at least no longer performs any operation that would access any UI objects. The typical way to do this is to tell the object that created and holds MyWindowController alive when the window has closed, and that object would then release MyWindowController and set its reference to nil or notify someone else and get destroy itself.

However, if you set isReleasedWhenClosed to false, then closing the window will not cause the window to receive a release message and thus not balance the self-retaining. In this case, the window would leak unless you add this code to the controller

- (void)dealloc
{
    [_window release];
    [super dealloc];
}

This was a bit odd because it violated the rules I just stated above. You did not create the window (you loaded the NIB file, but never created a window object yourself), and you did not retain it, so according to the rules above, you should never have to release it. Therefore, the default behavior was that the window would retain and release itself, and you didn’t have to worry about memory management at all.

Note that you only need to release the window itself, the label will be freed as a direct result of this. Releasing both will most likely crash or cause strange behavior. Why would you want this behavior? There are two situations where you might want this behavior:

  1. You may want to access UI objects after the window has closed, e.g. you may not want to read values from input fields immediately after the input field has been changed or after the user has clicked a button, but only after the window has closed. This is only possible if these UI objects still exist when the window is closed.

  2. You may want to bring the window back later without having to create a new controller and reload the window from the NIB file. All you need to do in the controller above is call [_window makeKeyAndOrderFront:self] and the window is back on the screen in exactly the state it was in when it was closed.

Now that you understand what this setting was originally good for and how it interacted with classical memory management, you might also understand why this setting is pretty much useless in modern Obj-C or Swift most of the time. With automatic reference counting in Obj-C and Swift, the window is automatically retained by your custom controller the moment it is assigned to an instance variable or property; and so is any other UI object within the window that you reference directly. Unless you manually set the references to nil or your controller object itself is destroyed, no window is ever destroyed when you set isReleasedWhenClosed to true and the window is closed.

In fact, the self-retain itself would be useless in modern code, with one exception: A weak reference to a window. If your window reference is weak (__weak NSWindow * _window) and the window would not self-retain, it would be destroyed immediately after creation and the variable would immediately become nil again. So there is still self-retaining, and if isReleasedWhenClosed is true, a weak variable will become nil the moment the window is closed (assuming no one else has retained it), otherwise it will not and still refer to the window.

Will a window leak in modern Obj-C and Swift if isReleasedWhenClosed is false? After all, you cannot explicitly release the window in ARC and in Swift, so how would you compensate for the self-retaining?

The answer is: Not if the owner is also destroyed. If you go back to the NIB loading example code, you may notice that an argument there was called owner and I just passed a self-reference to it. In modern Obj-C and Swift, the self-retain is released the moment the owner is destroyed. If there are no more strong references to the window when that happens, the window is released as well. But be careful: By default, the owner of the main window is the application delegate, and the application delegate is never destroyed. So if you lose all strong references to the main window and the main window is not set to release on close, it will actually leak.

Finally, there is one more thing that most people miss. The documentation says:

Release when closed, however, is ignored for windows owned by window controllers.

  • Source: https://developer.apple.com/documentation/appkit/nswindow/1419062-isreleasedwhenclosed

So if you are using a window controller, or if your own window controller is a subclass of a window controller, this property has no effect anyway. That’s because since MacOS 10.0, window controllers manage the lifecycles of their windows all by themselves and will prevent this setting from having any effect on the window retain count.

Summary

If you use a window controller or subclass of it, ignore that this setting even exists. Otherwise you almost certainly want isReleasedWhenClosed to be true in modern Obj-C and Swift code and manage the lifetime of the window yourself via strong references. The only reason to disable it is for the case that you (for whatever reason) have a weak reference only to the window and you don’t want it to become nil when the window is getting closed; but then you must make sure that the owner of the NIB files will get destroyed at some point, otherwise the window will leak.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật