5

我在这里遇到了一个奇怪的问题,我正在尝试将libkml C++ 项目源集成到我的 iOS 项目中。该项目可以独立编译,但是在通过这行代码进行链接时:

kmldom::PointPtr appPoint = kmlconvenience::CreatePointLatLon(appLocation.coordinate.latitude, appLocation.coordinate.longitude);

只有当我为模拟器构建它时,我才会收到链接器错误。当我为 iOS 设备构建它时它工作正常,但对于模拟器我得到以下 3 个链接器错误:

(null): "kmldom::GxTimeSpan::GxTimeSpan()", referenced from:

(null): Kmldom::KmlFactory::CreateGxTimeSpan() const in libLibKML.a(kml_factory.o)

(null): "kmldom::GxTimeStamp::GxTimeStamp()", referenced from:

(null): Kmldom::KmlFactory::CreateGxTimeStamp() const in libLibKML.a(kml_factory.o)

(null): Symbol(s) not found for architecture i386

(null): Linker command failed with exit code 1 (use -v to see invocation)

这些错误仅在我尝试为模拟器构建时发生

我可以单独为设备开发,但我希望解决这个问题有两个原因:

  1. 团队有时很容易将模拟器用于开发目的。
  2. 我真的很想深入了解它并了解为什么在第一种情况下会发生这种情况。为什么它为设备构建而为模拟器失败,尽管目标相同并且目标中包含的源文件在模拟器和设备之间是相同的

GXTimeStamp 和 GXTimeSpan 类的定义在头文件 gx_timeprimitive.h 中,内容如下:

#ifndef KML_DOM_GX_TIMEPRIMITIVE_H__
#define KML_DOM_GX_TIMEPRIMITIVE_H__

#include <string>
#include "kml/base/xml_namespaces.h"
#include "kml/dom/kml22.h"
#include "kml/dom/object.h"
#include "kml/dom/timeprimitive.h"

namespace kmldom {

class Serializer;
class Visitor;

// <gx:TimeSpan>
class GxTimeSpan : public TimeSpan {
 public:
  virtual ~GxTimeSpan();
  static KmlDomType ElementType() {
    return Type_GxTimeSpan;
  }
  virtual KmlDomType Type() const { return Type_GxTimeSpan; }
  virtual bool IsA(KmlDomType type) const {
    return type == Type_GxTimeSpan || TimeSpan::IsA(type);
  }

  // Visitor API methods, see visitor.h.
  virtual void Accept(Visitor* visitor);

 private:
  friend class KmlFactory;
  GxTimeSpan();
  LIBKML_DISALLOW_EVIL_CONSTRUCTORS(GxTimeSpan);
};

// <gx:TimeStamp>
class GxTimeStamp : public TimeStamp {
 public:
  virtual ~GxTimeStamp();
  static KmlDomType ElementType() {
    return Type_GxTimeStamp;
  }
  virtual KmlDomType Type() const { return Type_GxTimeStamp; }
  virtual bool IsA(KmlDomType type) const {
    return type == Type_GxTimeStamp || TimeStamp::IsA(type);
  }

  // Visitor API methods, see visitor.h.
  virtual void Accept(Visitor* visitor);

 private:
  friend class KmlFactory;
  GxTimeStamp();
  LIBKML_DISALLOW_EVIL_CONSTRUCTORS(GxTimeStamp);
};

}  // end namespace kmldom

#endif  // KML_DOM_GX_TIMEPRIMITIVE_H__

我读过很多帖子,因为源文件没有编译,所以会发生链接器错误。我也在考虑同样的问题来解决这个问题,但是我不能将此头文件包含到编译源中,因为它是一个 .h 文件。

另外,我仔细检查了 - kml_factory.cc 文件包含在内部项目的编译源中:

编译阶段包含的 kml_factory.cc 文件

期待建议和帮助。谢谢。

4

3 回答 3

3

这么说让我觉得很愚蠢,但是,我不知道 gx_timeprimitive.cc 文件是如何丢失的。我的印象是 gx_timeprimitive.h 文件本身是完整的,因为它具有虚拟类,我尝试通过在 Private 范围内提供构造函数的空实现来定义 GXTimeSpan 和 GXTimeStamp 类,如下所示:

class GxTimeSpan : public TimeSpan {
 public:
  virtual ~GxTimeSpan();
  static KmlDomType ElementType() {
    return Type_GxTimeSpan;
  }
  virtual KmlDomType Type() const { return Type_GxTimeSpan; }
  virtual bool IsA(KmlDomType type) const {
    return type == Type_GxTimeSpan || TimeSpan::IsA(type);
  }

  // Visitor API methods, see visitor.h.
  virtual void Accept(Visitor* visitor);

 private:
  friend class KmlFactory;
  GxTimeSpan()
  {

  }
  LIBKML_DISALLOW_EVIL_CONSTRUCTORS(GxTimeSpan);
};

但是,编译器仍然不会从接口文件中创建目标文件(.h 文件被排除在编译源之外,它们只包含所有声明),因此链接器无法找到它需要的构造函数。这让我在互联网上搜索 gx_timeprimitive.cc 文件,它确实可用。

冷静思考会为我节省 100 点赏金,但我有一个教训要学习!

另外,要回答为什么仅在模拟器模式下会出错:实际上是我上面提到的那一行- kmldom::PointPtr appPoint = kmlconvenience::CreatePointLatLon(appLocation.coordinate.latitude, appLocation.coordinate.longitude); 我认为,当为 ARMv7 构建时,除非该变量appPoint在代码中的其他地方使用,否则链接器会跳过将其与 libkml 对象的源链接文件。然而,无论变量是否在代码中使用,i386 都会执行链接。我想这是某种编译器优化,因此在各自的体系结构中行为不同。正是这个谜题让我错过了寻找丢失文件的关键线索!

向那些花时间解决我这个愚蠢问题的人道歉,谢谢大家。

于 2013-05-21T19:05:54.020 回答
2

这可能类似于i386 架构中未找到的 iOS 符号

以下是最相关的几点:

在为 iOS 构建库、框架或应用程序时,XCode 只会为目标的构建设置中指定的体系结构编译目标代码。XCode 也将仅链接到具有内置指定架构的二进制文件。

在 iOS 模拟器中运行代码时,您是在 i386 架构的桌面上运行代码。

如果您在模拟器中运行 iOS 应用程序时遇到缺少 i386 架构的错误,您需要确保您的应用程序及其所有依赖库都是针对 i386 架构构建的。

于 2013-05-20T18:32:54.157 回答