启用ARC时无xib/storyboard的macOS应用关闭窗口崩溃问题
崩溃的核心原因是ARC环境下AppDelegate实例无强引用持有,被提前释放引发野指针:NSApplication的delegate属性为弱引用(官方定义为@property (nullable, weak) id<NSApplicationDelegate> delegate;),弱引用不会增加对象引用计数,无法保证对象生命周期。
你在main.m中创建AppDelegate实例后直接赋值给delegate,没有任何其他强引用指向该实例,ARC会在赋值表达式结束后自动回收这个临时对象。后续App运行、关闭窗口触发终止流程时,NSApplication尝试向已经被释放的delegate发送回调消息,就会抛出EXC_BAD_ACCESS野指针错误。这也完全匹配你观察到的「仅开启ARC时崩溃」的现象:MRC模式下没有自动回收机制,创建的对象不会被主动释放,就不会触发该问题。
你之前尝试过用setDelegate:方法赋值但没有效果,和调用方式无关,本质是没有强引用托住AppDelegate实例。另外你写的(AppDelegate *){[[AppDelegate alloc] init]}复合字面量写法,在ARC下不会生成强引用持有临时对象,也会加剧这个问题。
只需要保证AppDelegate实例在App整个运行生命周期内被强引用持有即可,最简洁的实现是修改main.m,用强类型的局部变量持有delegate:
#import <Cocoa/Cocoa.h> #import "AppDelegate.h" int main(void) { @autoreleasepool { // 局部变量默认是__strong强引用,会在整个@autoreleasepool生命周期内持有对象 AppDelegate *appDelegate = [[AppDelegate alloc] init]; NSApplication *app = [NSApplication sharedApplication]; app.delegate = appDelegate; [app run]; } return 0; }
修改后appDelegate会在[NSApp run]整个执行周期(也就是从App启动到完全终止的全流程)里被强持有,不会被提前释放,关闭窗口时的回调就能正常触发,应用会按预期终止不会崩溃。
额外说明:你在
applicationDidFinishLaunching:中创建的NSWindow不会有同类问题,调用makeKeyAndOrderFront:后系统会把窗口加入NSApplication的window列表,被系统强持有,不会提前释放。唯一不规范的点是makeKeyAndOrderFront:的sender参数传了窗口自身,这个参数代表触发方法的调用者,传nil即可,不影响运行。
内容的提问来源于stack exchange,提问作者CPlus

