在子项目中配置条件代码
我有一个项目,有一个要导入的库子项目。我可以访问主项目和子项目的源代码。
该子项目使用Core Text。因为该子项目必须在 3.2 之前和之后的应用程序上使用,所以 Core Text 是弱链接的,并且所有与 Core Text 相关的代码都包装在逻辑中:
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
这行代码的思想是,如果 CoreText 没有链接进来,则代码被跳过。如果iPhone版本低于3.2; CoreText 未链接到。
此目标的原因是主项目(有几个)并不全部使用 Core Text,如果不使用,则不使用“已定义(CORETEXT)`,它们将无法编译。
这似乎工作正常,并且在使用 Core Text 的项目上一切编译都没有错误。然而,在执行时,找不到代码,并出现类似“NSString 不响应 XXXX”的运行时错误(部分代码是 NSString
上的类别)。
有人遇到过这个吗?我的问题由于是客户工作而部分模糊,清楚吗?
理想情况下,我想进行这样的设置,以便当主项目使用 Core Text 和不使用 Core Text 时都不需要更改子项目。
请注意,__CORETEXT__
是在 Core Text 框架的标头中定义的。
关于这个问题的更新
我已经尝试过迄今为止提供的建议,但它们是无效的。也许更多的代码将有助于概述问题。我有一个带有以下标题的类别:
@interface NSString (Internal)
- (NSString *)stringByUnescapingEntities;
- (NSString *)flattenHTML;
- (NSString *)flattenHTMLAndParseParagraphBreaks:(BOOL)parseBreaks;
- (NSString *)stringByEscapingForURL;
- (NSString *)stringByEscapingForJSON;
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
- (CFAttributedStringRef)attributedStringFromHTML;
- (CFAttributedStringRef)attributedStringFromHTMLWithBaseAttributes:(NSDictionary*)baseAttributes;
#endif
@end
Positive Test
#if
块之外的所有方法都可以完美运行。 #if
中的所有方法都完美地compile,没有警告。
负面测试
如果我将 __CORETEXT__
更改为 __THIS_IS_NOT_REAL__
之类的内容,我将收到块内部方法的编译错误。因此我知道,至少在编译时,__CORETEXT__
标志已定义且功能齐全。
运行时问题
但是,当我访问 #if 块内声明的方法之一时,我在运行时收到以下错误:
-[NSCFString attributeStringFromHTMLWithBaseAttributes:]:无法识别的选择器发送到实例 0x689c000
实例 0x689c000
是一个 NSString
。
如果我删除 __CORETEXT__
逻辑检查,那么只要主项目使用 Core Text,一切都会正常。如果主项目未使用 Core Text,则会显示有关缺少链接器的编译错误。
希望这能澄清这个问题。
注意:所有项目都使用 -all_load
标志;因为 TouchXML
已经需要它了。
我创建了一个测试项目来演示该问题。
I have a project that has a library subproject that gets imported. I have access to the source code of both the main project and the subproject.
The subproject uses Core Text. Because the subproject must be used on pre and post 3.2 applications, Core Text is weak linked and all of the Core Text related code is wrapped in the logic:
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
The thought of this line of code is that if CoreText is not linked in, the code is skipped. If the iPhone version is less than 3.2; CoreText is not linked in.
The reason for this goal is that the main project(s) (and there are several) do not all use Core Text and if the don't then without the 'defined(CORETEXT)`, they will not compile.
This seems to work fine and everything compiles without an error on projects that use Core Text. However on execution, the code is not found and a runtime error along the lines of "NSString does not respond to XXXX" (part of the code is a category on NSString
).
Has anyone run into this? Is my question, partially vague due it being client work, clear?
Ideally I want to set this up so that the subproject does not need to be altered when the main project uses Core Text and when it does not.
Note that __CORETEXT__
is defined in the header of the Core Text framework.
Update on the issue
I have tried the suggestions offered so far and they are ineffectual. Perhaps a little more code will help to outline the issue. I have a category with the following header:
@interface NSString (Internal)
- (NSString *)stringByUnescapingEntities;
- (NSString *)flattenHTML;
- (NSString *)flattenHTMLAndParseParagraphBreaks:(BOOL)parseBreaks;
- (NSString *)stringByEscapingForURL;
- (NSString *)stringByEscapingForJSON;
#if defined(__CORETEXT__) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
- (CFAttributedStringRef)attributedStringFromHTML;
- (CFAttributedStringRef)attributedStringFromHTMLWithBaseAttributes:(NSDictionary*)baseAttributes;
#endif
@end
Positive Test
All of the methods outside of the #if
block work perfectly. All of the methods inside of the #if
compile perfectly, no warnings.
Negative Test
If I change __CORETEXT__
to something like __THIS_IS_NOT_REAL__
I will get a compile error for the methods inside of the block. Therefore I know, at compile time at least, that the __CORETEXT__
flag is defined and fully functional.
Runtime issue
However, when I go to access one of the methods that are declared inside of the #if block, I get the following error at runtime:
-[NSCFString attributedStringFromHTMLWithBaseAttributes:]: unrecognized selector sent to instance 0x689c000
Instance 0x689c000
being a NSString
.
If I remove the __CORETEXT__
logic check then everything works so long as the master project is using Core Text. If the master project is not using Core Text then compile errors about missing linkers shows up.
Hopefully that clarifies the issue.
NOTE: All projects are using the -all_load
flag; as it was needed for TouchXML
already.
I have created a test project that demonstrates the issue.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
事实上,您指出至少最终丢失的一件事是 NSString 的类别方法,这让我认为您遇到了一个可爱的问题,这似乎是链接器中可能存在的错误。
首先,请参阅其他地方的答案并确保包括您的库的项目正在使用 -all_load 链接器标志。
其次,在我在 iPhone 上使用 Three20 库的一些交易中,有必要使用这种奇怪的“黑客”,例如,如果您的 NSString 类别是 .m 文件中唯一实现它的类别,则有必要使用这种奇怪的“黑客”。 。
然后类别 .h/.m ...
基本上似乎存在与静态库相关的附加问题,其中实现文件仅包含类别方法的实现。 FIX_CATEGORY_BUG 宏基本上只是扩展为定义一个接口和该接口的实现,没有方法,只是将一些不是类别的内容强制放入 .m 文件中。
The fact that you call out that at least one thing that ends up missing is a category method for NSString makes me think you're running into a lovely issue that seems to be a possible bug in the linker.
Firstly, see this answer elsewhere and ensure that the project including your library is using the -all_load linker flag.
Second, in some dealings I've had with using the Three20 library for the iPhone it became necessary to also use this weird "hack" that applies if, for example, your NSString category is the only thing in the .m file where it's implemented.
Then the category .h/.m ...
Basically there seems to be an additional issue related to static libraries where an implementation file that contains ONLY implementations of category methods. The FIX_CATEGORY_BUG macro basically just expands to define an interface and implementation of that interface with no methods, just to force something into the .m file that isn't a category.
您可以执行 [NSObject respondsToSelector:] 检查以查看类别方法是否可用。
编辑:我想我第一次没有完全阅读这个问题。这是我看到的另外一些事情。
#if
可能行不通。这是因为这都是在编译时而不是运行时检查的。我不太确定弱链接时会包含哪些内容,但据我了解,弱链接框架的标头将在编译时进行处理。这意味着方法原型的符号在您构建时将可用,这就是为什么没有构建错误或警告的原因。我也 99% 确信 iOS 检查不会起作用。这只检查允许的最大版本,这是在编译时设置的。除非您为每个 iOS 使用不同的设置进行不同的构建(我认为不是),否则无论您在哪种设备上运行,它几乎总是相同的值。
您肯定需要运行时检查(例如前面提到的respondsToSelector)。 iOS 设备的版本检查也只能在运行时完成。
You could do [NSObject respondsToSelector:] checks to see if the category method is available or not.
Edit: I think I didn't fully read the question the first time around. Here are a couple more things that I saw.
That
#if
probably won't work. This is because this is all checked at compile-time rather than run-time. I'm not exactly sure on what gets included in when weak-linking, but as I understand it, the header of the weak-linked framework will get processed at compile-time. That means the symbols for the method prototypes will be available when you build, which is why there are no build errors or warnings.I'm also 99% sure that the iOS check will not work. That only checks the max version allowed, which is set at compile-time. Unless you're making different builds with different settings for each iOS (which I assume not), it's pretty much always going to be the same value regardless of which device you're running on.
You're definitely going to need runtime checks (e.g. the afore mentioned respondsToSelector). Version checking of the iOS device can only be done at runtime as well.
我记得我做了一些与大卫类似的事情,但只是为了一种方法。
我使用 Objective-C 运行时函数来调用该方法,因此它可以在每个版本中进行编译。
我做了类似的事情:
不是我写过的最漂亮的代码,也不是我以前尝试过的,但也许可以使用运行时函数(如 class_addIvar、class_addMethod 等)添加你的方法和 ivars。
I remember I did something similar to David but just for a single method.
I used Objective-C runtime functions to call that method so it will compile in every version.
I did something like:
Not the most beautiful code I have written, nor I have tried before but maybe is possible to add your methods and ivars with runtime functions like class_addIvar, class_addMethod, etc.
如果该类别位于子项目中,您可能需要设置该项目的目标。在项目中执行子项目时,我遇到过这样奇怪的副作用。
编辑:意思是,子项目可以设置为针对 3.1.3 进行编译,并且可能永远不会符合您的逻辑?
If the category is housed in the sub project, you may need to set the target of that project. I have had weird side effects like this when doing sub projects within a project.
Edit: Meaning, the subproject could be set to compile against 3.1.3 and possible never meets your logic?
也许您应该将条件设置为
毕竟,引用项目的其余部分已链接进来,对吗?问题一定是预处理器的条件。
Maybe you should make the conditional into
After all, the rest of the referenced project is linked in, correct? The problem must be the preprocessor's conditional.
我相信我最近发现了类似的情况,正如我在 twitter 上告诉你的那样,我认为除了 -all_load 之外,你还需要设置 -ObjC,如下所述: http://developer.apple.com/mac/library/qa/qa2006/qa1490.html
I believe I found a similar situation recently, and as I told you on twitter I think you need to set -ObjC in addition to -all_load, as described here: http://developer.apple.com/mac/library/qa/qa2006/qa1490.html
答案分为两部分:
CoreText.h
导入到应用程序的 PCH 文件中,但不在库中(PCH 不级联)。因此,在库中,__CORETEXT__
代码始终被#ifdef
剔除。但是,当在应用程序中编译从库导出的标头时,会包含#ifdef
的方法原型,因此您不会得到 NSString 上该方法的编译时警告。对此的快速解决方法是放置一个文字
在您图书馆的 PCH 文件中。当然,这违背了您尝试做的事情的目的。
解决这个问题是另一个主题,但我怀疑你最好使用自己的 -D 或 #define,然后尝试级联你的子项目,尽管我不知道如何我自己也这样做。
临时测试您的
#ifdef
代码是否正在编译的一个好方法是将#error
放入条件中,例如<前><代码>#ifdef WHEE
// 这里会发生编译时错误
#错误
#endif
An answer in two parts:
CoreText.h
is imported in your app's PCH file, but nowhere in the library (PCH's do not cascade). Therefore, in the library the__CORETEXT__
code is always#ifdef
'd out. However, when the exported header from the library is compiled in the App, the#ifdef
'd method prototype is included, and therefore you get no compile-time warnings for that method on NSString.A quick fix for this is putting a literal
in your library's PCH file. Which of course, defeats the purpose of what you are trying to do.
Fixing that is another subject, but I suspect you would be better off using your own -D or
#define
, and then trying to cascade that your subproject, though I don't know how to do that myself, yet.One good way to temporarily test if your
#ifdef
'd code is compiling is putting#error
's in the conditionals, e.g.