Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 为什么这个简单的NSWindow创建代码会在ARC关闭时触发自动释放池崩溃?_Objective C_Macos_Crash_Automatic Ref Counting_Clang - Fatal编程技术网

Objective c 为什么这个简单的NSWindow创建代码会在ARC关闭时触发自动释放池崩溃?

Objective c 为什么这个简单的NSWindow创建代码会在ARC关闭时触发自动释放池崩溃?,objective-c,macos,crash,automatic-ref-counting,clang,Objective C,Macos,Crash,Automatic Ref Counting,Clang,我遇到了一个关机时自动释放池崩溃的问题,我把它简化为下面的小测试用例,它只创建一个窗口,然后关闭它。如果取消-fobjc arc标志,则碰撞消失。在OSX10.8.2、Clang4.1(421.11.66)上运行。我希望对ARC有更深入了解的人能告诉我这里发生了什么-在节目中运行僵尸对象,是NSWindow对象被释放了太多次,或者保留得不够,但我认为ARC是为了处理所有这些 堆栈跟踪是: 0 libobjc.A.dylib 0x00007fff8fad4f5e

我遇到了一个关机时自动释放池崩溃的问题,我把它简化为下面的小测试用例,它只创建一个窗口,然后关闭它。如果取消
-fobjc arc
标志,则碰撞消失。在OSX10.8.2、Clang4.1(421.11.66)上运行。我希望对ARC有更深入了解的人能告诉我这里发生了什么-在节目中运行僵尸对象,是NSWindow对象被释放了太多次,或者保留得不够,但我认为ARC是为了处理所有这些

堆栈跟踪是:

0   libobjc.A.dylib                 0x00007fff8fad4f5e objc_release + 14
1   libobjc.A.dylib                 0x00007fff8fad4230 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 464
2   com.apple.CoreFoundation        0x00007fff99d22342 _CFAutoreleasePoolPop + 34
3   com.apple.Foundation            0x00007fff936e84fa -[NSAutoreleasePool drain] + 154
4   com.apple.Foundation            0x00007fff936effa0 _NSAppleEventManagerGenericHandler + 125
5   com.apple.AE                    0x00007fff93a5ab48 aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) + 307
6   com.apple.AE                    0x00007fff93a5a9a9 dispatchEventAndSendReply(AEDesc const*, AEDesc*) + 37
7   com.apple.AE                    0x00007fff93a5a869 aeProcessAppleEvent + 318
8   com.apple.HIToolbox             0x00007fff8d0c18e9 AEProcessAppleEvent + 100
9   com.apple.AppKit                0x00007fff8e95c916 _DPSNextEvent + 1456
10  com.apple.AppKit                0x00007fff8e95bed2 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 128
11  com.apple.AppKit                0x00007fff8e953283 -[NSApplication run] + 517
12  Test                            0x00000001070e1d68 main + 152 (Test.mm:31)
13  libdyld.dylib                   0x00007fff8e10c7e1 start + 1
测试用例的代码是:

// Tested with `clang++ -fobjc-arc -g Test.mm -framework Cocoa -o Test && ./Test`

#import <Cocoa/Cocoa.h>

@interface MyApplication : NSApplication
@end
@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
    NSWindow * window = [[NSWindow alloc] initWithContentRect: NSMakeRect(100, 100, 100, 100)
                        styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: YES];

    [window close];

    [super stop: self];
}
@end

int main()
{
    @autoreleasepool
    {
        const ProcessSerialNumber psn = { 0, kCurrentProcess };
        TransformProcessType(&psn, kProcessTransformToForegroundApplication);
        SetFrontProcess(&psn);

        [MyApplication sharedApplication];
        [NSApp setDelegate: NSApp];

        [NSApp run];
    }

    return 0;
}
//使用`clang++-fobjc-arc-g Test.mm-framework-Cocoa-o Test&&&&./Test进行测试`
#进口
@接口MyApplication:NSApplication
@结束
@实现我的应用程序
-(无效)ApplicationIDFinishLaunching:(NSNotification*)注释
{
NSWindow*window=[[NSWindow alloc]initWithContentRect:NSMakeRect(100100100100)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:YES];
[关窗];
[超级停止:自我];
}
@结束
int main()
{
@自动释放池
{
const ProcessSerialNumber psn={0,kCurrentProcess};
TransformProcessType(&psn、kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
[MyApplication sharedApplication];
[NSApp setDelegate:NSApp];
[NSApp运行];
}
返回0;
}

如果将新创建的对象分配给范围大于当前范围的变量,ARC将仅保留该对象。否则,对象将泄漏

在您的示例中,您正在通过调用alloc来创建
NSWindow
的新实例,alloc临时将所有权转移到局部变量
window
。由于该变量在方法末尾不再存在,ARC必须插入
release
调用以避免泄漏窗口实例。因此,该实例不再为任何东西所有,因此将自行解除分配

要解决此问题,请使用
strong
语义声明一个类型为
NSWindow
的属性,并将窗口实例传递给属性setter方法(或直接将其分配给相应的实例变量——两者都可以)

编辑

需要明确的是,您需要做的是添加一个声明的属性(或至少一个实例变量)到
MyApplication
,例如

@interface MyApplication : NSApplication

@property (strong, nonatomic) NSWindow *window;

@end
然后,在
ApplicationIDFinishLaunching
的实现中,设置以下属性:

@implementation MyApplication

- (void) applicationDidFinishLaunching: (NSNotification *) note
{
    NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(100, 100, 100, 100)
                        styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:YES];

    self.window = window;

    ...
}

@end

有了ARC,您需要为那些被认为会漂浮更长时间的东西使用属性

要抽象私有属性,请在.m文件中使用匿名类别

这里有更多的信息

这就解决了这个问题:

@interface MyApplication : NSApplication
@property NSWindow *window;

@end

@implementation MyApplication
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
  self.window = [[NSWindow alloc] initWithContentRect: NSMakeRect(100, 100, 100, 100)
                                                  styleMask: NSTitledWindowMask backing: NSBackingStoreBuffered defer: YES];

  [self.window close];

  [super stop: self];
}
@end

int main()
{
  @autoreleasepool
  {
    const ProcessSerialNumber psn = { 0, kCurrentProcess };
    TransformProcessType(&psn, kProcessTransformToForegroundApplication);
    SetFrontProcess(&psn);

    [MyApplication sharedApplication];
    [NSApp setDelegate: NSApp];

    [NSApp run];
  }

  return 0;
}

希望有帮助

使用Instruments的Zombies配置文件显示,通过调用
close:
,NSWindow对象被放入自动释放池。然后,ARC在完成并销毁NSWindow实例后,正确地以零引用计数结束。但是,自动释放池仍然知道现在已失效的NSWindow实例,然后在关闭时尝试释放它,从而导致崩溃

自动释放在ARC下管理的对象似乎不是一个好主意,除非自动释放池保留对其对象的零弱引用,而在这里它似乎没有这样做


通过添加
[window setReleasedWhenClosed:NO],告诉窗口关闭时不要自动释放,可以防止该问题

同意,如果我使用的窗口超出了
applicationdFinishLaunching
的范围,则实例将需要更大范围的强引用,但本例只是尝试创建一个窗口,然后关闭它。在这个测试用例中,
NSWindow
对象不在这个方法之外的任何地方使用。此外,将
\uu strong
添加到
窗口的定义中也没有任何区别,可能是因为默认情况下已经存在了。这有意义吗?如果窗口实例不存在于
ApplicationIDFinishLaunching
的范围之外,您将永远无法使用它,因为它将立即被释放。为了使用该窗口,您必须确保它属于应用程序中的某个对象。局部变量
窗口
仅在方法范围结束前存在。ARC然后向窗口实例发送一条
release
消息。因为此时窗口没有所有者,所以它会释放自己。您需要将窗口存储在一个实例变量中,以防止其释放自身。顺便说一句,您的代码在禁用ARC的情况下工作的原因是,如果没有ARC,则窗口在
ApplicationIDFinishLaunching
结束时泄漏。此时,对窗口实例的唯一引用是局部变量
window
,该变量在方法末尾不再存在。由于您的代码不再知道窗口的地址,因此永远无法向其发送
发布
消息,因此永远无法解除分配。是的,此示例是精心设计的,只是为了说明崩溃,而不是实际窗口管理的示例!而且,在禁用ARC的情况下,窗口在技术上不会泄漏,
[window close]
将其放入自动释放池中,因此当自动释放池结束时,它将在
main()
中释放。谢谢。这对ARC应用程序来说是一个很好的一般性建议,但从我所知道的来看,在这个例子中,并不是崩溃的原因。我想我最终找到了答案,请参阅我刚刚发布的答案,了解详细情况。谢谢。那么,给我们至少+1个Richard。另外,根据您自己的评论,使用属性实际上也可以解决这个问题。而且是被推荐的,所以我还是