在子项目中配置条件代码

时间:2010-08-26 20:19:55

标签: objective-c xcode ios 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,如果没有那么没有'defined( CORETEXT )` ,他们不会编译。

这个似乎可以正常工作,所有内容在使用Core Text的项目上编译时没有错误。但是在执行时,找不到代码,并且“NSString”中的运行时错误不响应XXXX(代码的一部分是NSString上的类别)。

有没有人碰到这个?我的问题,部分模糊,因为它是客户工作,清楚吗?

理想情况下,我想进行设置,以便在主项目使用核心文本时不需要更改子项目,而不需要更改子项目。

请注意,__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

阳性测试

#if块之外的所有方法都能正常工作。 #if 编译内的所有方法都完美无误,没有警告。

否定测试

如果我将__CORETEXT__更改为__THIS_IS_NOT_REAL__之类的内容,我将会遇到块内部方法的编译错误。因此,我至少在编译时知道__CORETEXT__标志已定义且功能完全正常。

运行时问题

但是,当我去访问#if块中声明的方法之一时,我在运行时遇到以下错误:

  

- [NSCFString attributionStringFromHTMLWithBaseAttributes:]:无法识别的选择器发送到实例0x689c000

实例0x689c000NSString

如果我删除__CORETEXT__逻辑检查,那么只要主项目使用Core Text,一切都可以正常工作。如果主项目未使用Core Text,则会显示有关缺少链接器的编译错误。

希望澄清这个问题。

注意:所有项目都使用-all_load标记;因为TouchXML已经需要它。

我创建了一个演示此问题的测试项目。

Test Project

7 个答案:

答案 0 :(得分:2)

你提到至少有一件东西最终缺失的事实是NSString的类别方法让我觉得你遇到了一个可爱的问题,这似乎是链接器中可能存在的错误。

首先,请参阅this answer elsewhere并确保包含您的库的项目使用-all_load链接器标记。

其次,在我使用iPhone的Three20库进行的一些交易中,有必要使用这种奇怪的“黑客”,例如,如果您的NSString类别是.m文件中的唯一内容它实施的地方。

//CategoryBugHack.h
#define FIX_CATEGORY_BUG(name) @interface FIX_CATEGORY_BUG##name @end @implementation FIX_CATEGORY_BUG##name @end

然后是类别.h / .m ...

//NSStringCategory.h
@interface NSString (MyAdditions)
// etc, etc
@end

//NSStringCategory.m
#import "CategoryBugHack.h"
FIX_CATEGORY_BUG(NSString_MyAdditions)
@implementation NSString (MyAdditions)
// etc, etc
@end

基本上似乎存在与静态库相关的其他问题,其中实现文件仅包含类别方法的实现。 FIX_CATEGORY_BUG宏基本上只是扩展来定义一个接口和没有方法的接口的实现,只是强迫某些东西进入不属于类别的.m文件。

答案 1 :(得分:1)

您可以执行[NSObject respondsToSelector:]检查以查看类别方法是否可用。

编辑:我想我第一次没有完全阅读这个问题。以下是我看到的更多内容。

#if可能无效。这是因为这是在编译时而不是运行时检查的。我不确定弱链接时包含什么,但据我了解,弱链接框架的头将在编译时得到处理。这意味着在构建时可以使用方法原型的符号,这就是没有构建错误或警告的原因。

我也99%确定iOS检查不起作用。它只检查允许的最大版本,这是在编译时设置的。除非您使用不同的设置为每个iOS制作不同的版本(我假设不是这样),否则无论您运行的是哪个设备,它都将始终具有相同的值。

你肯定需要运行时检查(例如前面提到的respondsToSelector)。 iOS设备的版本检查也只能在运行时完成。

答案 2 :(得分:0)

也许你应该将条件转化为

#if defined(__CORETEXT__) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2)

毕竟,引用项目的其余部分已链接,对吗?问题必须是预处理器的条件。

答案 3 :(得分:0)

我记得我做过类似大卫的事情,但只是针对一种方法。 我使用Objective-C运行时函数来调用该方法,以便在每个版本中进行编译。 我做了类似的事情:

if ([theReceiver respondToSelector:@selector(mySelector:)]){
   objc_msgSend(theReceiver, @selector(mySelector:), param1);
}else{
   // do it other way here.
}

不是我编写的最漂亮的代码,也不是我之前尝试过的,但也许可以使用运行时函数(如class_addIvar,class_addMethod等)添加方法和ivars。

答案 4 :(得分:0)

如果类别位于子项目中,您可能需要设置该项目的目标。在项目中进行子项目时,我有这样奇怪的副作用。

编辑:意思是,子项目可以设置为针对3.1.3进行编译,可能永远不符合你的逻辑?

答案 5 :(得分:0)

我相信我最近发现了类似的情况,正如我在twitter上告诉你的那样,我认为除了-all_load之外你还需要设置-ObjC,如下所述:http://developer.apple.com/mac/library/qa/qa2006/qa1490.html

答案 6 :(得分:0)

答案分为两部分:

  • CoreText.h导入到您应用的PCH文件中,但库中没有(PCH不会级联)。因此,在库中,__CORETEXT__代码始终为#ifdef'。但是,当在库中编译导出的标头时,会包含#ifdef'd 方法原型,因此您没有 编译时< / em> NSString上该方法的警告。

    快速解决此问题的方法是输入文字

    #import <CoreText/CoreText.h>
    

    在您图书馆的PCH文件中。当然,这会破坏你想要做的目的。

    修复这是另一个主题,但我怀疑你最好使用你自己的-D或#define,然后尝试级联你的子项目,虽然我不知道如何自己做,爱好。

  • 临时测试你的#ifdef代码是否正在编译的一个好方法是将#error放入条件中,例如

    #ifdef WHEE
         // compile-time error will happen here
         #error
    #endif