objective-c - Cocoa 类成员变量在函数调用内部分配 nil 除非强制初始化/加载

标签 objective-c macos cocoa memory-management automatic-ref-counting

我有 C/C++ 背景,目前正在学习一些有关 Cocoa 和 Objective-C 的知识。

我有一个奇怪的行为,涉及延迟初始化(除非我弄错了),并且感觉我错过了一些非常基本的东西。

设置:

  • Xcode 10.1 (10B61)
  • macOS High Sierra 10.13.6
  • 从头开始 Cocoa 项目
  • 使用 Storyboard
  • 添加文件TestMainView.m/.h
  • 在main.storyboard中的View Controller下,将NSView自定义类设置为TestMainView
  • 在调试和发布版本下进行测试

基本上,我在 View Controller 内创建一个 NSTextView 以便能够编写一些文本。 在 TestMainView.m 中,我以编程方式创建对象链,如 here 中所述。

有两条路:

  • 第一个通过将 USE_FUNCTION_CALL 设置为 0 来启用,它使整个代码在 awakeFromNib() 内运行。
  • 通过将 USE_FUNCTION_CALL 设置为 1 来启用第二条路径。它使文本容器和 TextView 通过函数调用 addNewPage() 进行分配,并返回文本容器以供进一步使用。

第一个代码路径按预期工作:我可以编写一些文本。

但是第二个代码路径不起作用,因为返回时,textContainer.textView 为零(textContainer 值本身完全没问题)。

更麻烦的是(这就是我怀疑惰性 init 是罪魁祸首的地方)是,如果我在函数调用内部“强制”使用 textContainer.textView 值,那么一切都会正常工作美好的。您可以通过将 FORCE_VALUE_LOAD 设置为 1 来尝试此操作。

它不一定是 if(),它也可以与 NSLog() 一起使用。如果您在返回行设置断点并使用调试器打印值(“p textContainer.textView”),它甚至可以工作

所以我的问题是:

  • 这与延迟初始化有关吗?
  • 这是一个错误吗?有解决方法吗?
  • 我是否以错误的方式思考 Cocoa/ObjC 编程?

我真的希望我在这里遗漏了一些东西,因为不能指望我随机检查 Cocoa 类中的变量,希望它们不会变成 nil。它甚至默默地失败(没有错误消息,什么也没有)。

TestMainView.m

#import "TestMainView.h"

#define USE_FUNCTION_CALL 1
#define FORCE_VALUE_LOAD 0

@implementation TestMainView

NSTextStorage* m_mainStorage;

- (void)awakeFromNib
{
    [super awakeFromNib];

    m_mainStorage = [NSTextStorage new];
    NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
#if USE_FUNCTION_CALL == 1
    NSTextContainer* textContainer = [self addNewPage:self.bounds];
#else
    NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(FLT_MAX, FLT_MAX)];

    NSTextView* textView = [[NSTextView alloc] initWithFrame:self.bounds textContainer:textContainer];
#endif
    [layoutManager addTextContainer:textContainer];
    [m_mainStorage addLayoutManager:layoutManager];

    // textContainer.textView is nil unless forced inside function call
    [self addSubview:textContainer.textView];
}

#if USE_FUNCTION_CALL == 1
- (NSTextContainer*)addNewPage:(NSRect)containerFrame
{
    NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(FLT_MAX, FLT_MAX)];

    NSTextView* textView = [[NSTextView alloc] initWithFrame:containerFrame textContainer:textContainer];
    [textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];

#if FORCE_VALUE_LOAD == 1
    // Lazy init ? textContainer.textView is nil unless we force it
    if (textContainer.textView)
    {

    }
#endif
    return textContainer;
}
#endif

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.
}

@end

TestMainView.h

#import <Cocoa/Cocoa.h>

NS_ASSUME_NONNULL_BEGIN

@interface TestMainView : NSView

@end

NS_ASSUME_NONNULL_END

最佳答案

我对 cocoa 不太熟悉,但我认为问题是 ARC(自动引用计数)。

NSTextView* textView = [[NSTextView alloc] initWithFrame:containerFrame textContainer:textContainer];

在NSTextContainer的.h文件中可以看到NSTextView是弱引用类型。

enter image description here

因此从函数返回后它会被释放

但是,如果您将 textView 设置为 TestMainView 的实例变量,它将按预期工作。 不太确定为什么如果你强制它也能起作用。 ~~(也许是编译器优化?)~~

这似乎是强制,即打电话

if (textContainer.textView) {

正在触发保留/自动释放调用,因此直到下一个自动释放耗尽调用之前,textview 仍然处于事件状态。(我猜它在 awakeFromNib 函数返回之前不会被耗尽)。它起作用的原因是您在自动释放池释放它之前将 textView 添加到 View 层次结构(强引用)。

关于objective-c - Cocoa 类成员变量在函数调用内部分配 nil 除非强制初始化/加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54465894/

相关文章:

objective-c - tableview 上的 setediting = true 使单元格附件 View 消失?

ios - OS X 10.11 beta 6 和 Xcode 问题

mysql - PHPStorm 无法连接到本地 MySQL

Cocoa Xcode::如何本地化我的应用程序中的警报?

cocoa - 如何在 Swift 中从 selectedRowIndexs 中获取一个整数?

ios - 如何解决JSON字典

ios - 如何在 NSUserDefaults 中保存 NSMutablearray

objective-c - 这些数据是否被安全地发送?

objective-c - cocoa 中的自定义主应用程序循环

iphone - iOS 5 UIButton 标题显示省略号