通过属性getter访问std :: map会返回垃圾值

时间:2014-05-22 10:01:03

标签: c++ ios llvm objective-c++ xcode5.1

我在项目中遇到了这个问题,并将其归结为以下示例代码:

#import <map>

typedef std::map<int, int> exampleMap;
@interface ViewController ()

@property (nonatomic, assign) exampleMap map;
@end

@implementation ViewController

- (IBAction)iterateWithSelf:(id)sender
{
    for (exampleMap::iterator iter = self.map.begin(); iter != self.map.end(); ++iter)
    {
        NSLog(@"We should never hit this line as map is empty");
    }
}
- (IBAction)iterateWithIVar:(id)sender
{
    for (exampleMap::iterator iter = _map.begin(); iter != _map.end(); ++iter)
    {
        NSLog(@"We should never hit this line as map is empty");
    }
}

@end

- iterateWithIVar:执行正常,并且没有任何内容记录到控制台。 - iterateWithSelf:进入for循环,在控制台上打印一行,然后与EXC_BAD_ACCESS

崩溃

我尝试插入... self.map.insert(std::pair<int, int>(0, 1));似乎在_map.insert(std::pair<int, int>(0, 1));iterateWithIvar添加迭代时没有任何影响

有什么想法吗?

1 个答案:

答案 0 :(得分:5)

需要注意的重要一点是self.map发送消息(即调用方法)。 它不访问结构成员。它类似于以下C ++代码:

class ViewController {
public:
  typedef std::map<int, int> ExampleMap;

private:
  ExampleMap _map;

public:
  ExampleMap map() { return _map; }
  setMap(const ExampleMap &o) { _map = o; }
};

接着是

for (auto iter = vc.map().begin(); iter != vc.map().end(); ++iter) {
  std::cerr << "We should never hit this line as map is empty" << std::endl;
}

在这种情况下,这是“预期的行为”。 C ++对象按值复制。这意味着每次拨打self.map时,您都会获得地图的新副本。因为从一个地图使用迭代器迭代第二个地图是无效的C ++,所以你会崩溃。

在第二种情况下,您可以直接访问实例变量,因此每次都要处理同一个对象。有几种解决方法:

  1. 重写您的课程,以提供直接访问ivar的访问者(例如-(int) numberOfItemsInMap-mapItemForKey:等)。

  2. 更改访问者不再复制:使用ivar作为地图,手动编写自己的getter以返回指针或对地图的引用(如果使用指针,则可以将访问器声明为指针属性无论如何,但是然后使用operator new在你的构造函数中创建它。)

  3. 重写-iterateWithSelf:以获取地图的显式副本,然后对其进行迭代(假设std::map是您的用例的有效容器,这可能会效率低下)。

  4. 第一种可能是最干净的方法,因为它还确保您的ObjC对象始终了解对底层地图的访问/更改,而直接暴露指向地图的指针允许任何人在您不知情的情况下修改它。

相关问题