Я столкнулся с этой проблемой в проекте и свел ее к следующему примеру кода:
#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
Есть идеи?
Важно отметить, что 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 ++ для итерации по второй карте, возникает сбой.
Во втором случае вы напрямую обращаетесь к переменной экземпляра, так что вы имеете дело с одним и тем же объектом каждый раз. Есть несколько обходных путей:
Перепишите свой класс, чтобы предоставить средства доступа (например, -(int) numberOfItemsInMap
, -mapItemForKey:
и т. д.), которые имеют прямой доступ к ивару.
Измените методы доступа, чтобы они больше не копировались: используйте ivar для карты, вручную напишите собственный метод получения, чтобы вернуть указатель или ссылку на карту (если вы используете указатель, вы можете в любом случае объявить методы доступа как свойства указателя, но затем используйте оператор новый, чтобы создать его в вашем конструкторе).
перезапись -iterateWithSelf:
взять явную копию карты, а затем выполнить итерацию (вероятно, это будет неэффективно, если предположить, что std::map
является эффективным контейнером для вашего случая использования).
Первый, вероятно, самый чистый подход, так как он также гарантирует, что ваш объект ObjC всегда знает о доступах / изменениях, внесенных в основную карту, тогда как непосредственное экспонирование указателя на карту позволяет любому изменять его без вашего ведома.