【问题标题】:EXC_BAD_ACCESS calling c++ class method from NSTimerEXC_BAD_ACCESS 从 NSTimer 调用 c++ 类方法
【发布时间】:2015-02-28 13:56:48
【问题描述】:

在使用 Xcode4 构建可可应用程序时尝试混合 c++ 和 Objective-c 时遇到问题。 问题是当我使用 NSTimer 调用 handleFrame 函数时,它调用了一个类的虚函数。

这是我想要做的: 1.创建监视器; 2.创建处理程序; 3. 将处理程序分配给监视器(init 函数) 4. 调用monitor->update() 预期调用handler 的虚方法。 5. 代码在 applicationDidFinishLaunching 函数中按预期工作,但是 NSTimer 在 handleFrame 中导致 EXC_BAD_ACCESS 异常。

    //
    //  AppDelegate.h
    //  Concept5
    //

    #import <Cocoa/Cocoa.h>
    #include "monitor.h"
    #include "Derived.h"

    @interface AppDelegate : NSObject <NSApplicationDelegate>
    {
        Monitor *monitor;`enter code here`
        NSTimer *gameTimer;
    }

    @property (assign) IBOutlet NSWindow *window;

    - (void)handleFrame:(NSTimer *)timer;

    @end

AppDelegate implementation (.mm)

    //
    //  AppDelegate.mm
    //  Concept5
    //

    #import "AppDelegate.h"

    @implementation AppDelegate


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

    - (id) init {
        self = [super init];
        if(self) {
            monitor = new Monitor();
        }
        return self;
    }

    - (void)handleFrame:(NSTimer *)timer {
         monitor->update();
    }

    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        // Insert code here to initialize your application
        Derived derived;

        monitor->init(derived);
        monitor->update();

        gameTimer = [[NSTimer scheduledTimerWithTimeInterval:1
                                                      target:self
                                                    selector:@selector(handleFrame:)
                                                    userInfo:nil
                                                     repeats:YES] retain];


        monitor->update();

    }
    @end





    //
    //  Monitor.cpp
    //  Concept5
    //

    #include "Monitor.h"


    void Monitor::init (Base& handler)
    {
        _handler = &handler;
    }

    void Monitor::update()
    {
        if (_handler != NULL)
        {
            _handler->speak(); // <-- EXC_BAD_ACCESS exception.
        }
    }

//
//  Monitor.h
//  Concept5

#ifndef __Concept5__Monitor__
#define __Concept5__Monitor__

#include <iostream>
#include "Base.h"

class Monitor
{
private:
    Base* _handler;
public:
    void init (Base& handler);
    void update();
};

#endif /* defined(__Concept5__Monitor__) */

    //
    //  Base.cpp
    //  Concept5


    #include "Base.h"

    void Base::speak()
    {
        std::cout << "Base speaks" << std::endl;
    }


    //
    //  Base.h
    //  Concept5


    #ifndef __Concept5__Base__
    #define __Concept5__Base__

    #include <iostream>

    class Base
    {
    public:
         virtual void speak();
    };

    #endif /* defined(__Concept5__Base__) */

    //
    //  Derived.cpp
    //  Concept5


    #include "Derived.h"

    void Derived::speak()
    {
        std::cout << "Derived speaks" << std::endl;
    }

    //
    //  Derived.h
    //  Concept5
    //

    #ifndef __Concept5__Derived__
    #define __Concept5__Derived__

    #include <iostream>
    #include "Base.h"

    class Derived : public Base
    {
    public:
        void speak();
    };
    #endif /* defined(__Concept5__Derived__) */

【问题讨论】:

  • 更新:在 NSTimer 触发之前,_handler 类型是 Derived(预期),但在 NSTimer 触发之后,_handler 类型是 Base。
  • 你有一个构造函数,它接受一个地址并存储它。在 init() 函数和 update() 函数之间的过渡期间,没有迹象表明该地址指向的内容发生了什么或发生了什么。然后在更新函数中测试 not NULL,但如果指向的对象无效,则该测试毫无价值。
  • 可能是在 applicationDidFinishLaunching 中创建的派生对象超出范围并在 applicationDidFinishLaunching 完成时被销毁?并且它的引用没有被monitor的实例保留?
  • 可能就是这个问题。我在下面发布了一个答案,尽管我不是 Objective-C 程序员。

标签: c++ objective-c xcode macos


【解决方案1】:

我从未使用过 Objective-C,但以下看起来像是一个问题:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    Derived derived;
    monitor->init(derived);
    //....
}

由于Derived 是一个局部变量,作用域不会超出applicationDidFinishLaunching 函数。因此,当上述函数返回时,调用init()(它接受一个指针)将持有一个无效对象。

如果这是 C++,解决方案是确保对象的生命周期足够长。通常的解决方案是:

1) 使对象成为全局对象,或者

2) 使用new 动态创建对象,或者

3) 创建一个智能指针(可能是std::shared_ptr)并使用它来代替原始指针。

【讨论】:

    【解决方案2】:

    我不是 Objective-C 专家,但 EXC_BAD_ACCESS 表示您正在尝试使用错误指针(可能是 nil 指针)访问某些内容。

    由于您的计时器在 Monitor 实例上调用的是 INSTANCE 方法而不是 CLASS 方法,因此您最好在计时器触发时拥有一个 Monitor 实例。我的猜测是你不知道。如果我更像是一个 Objective-C 的人,我可能会看看你的代码并看到这个,但事实上我必须运行你的代码才能知道。但我敢打赌,这就是问题所在。

    话虽如此,使用实例方法调用计时器是一个冒险的想法,除非您绝对确定实例仍然存在并且您知道自己在做什么。只在计时器上调用类方法更安全。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多