在子项目中配置条件代码

发布于 2024-09-16 07:53:56 字数 2211 浏览 6 评论 0原文

我有一个项目,有一个要导入的库子项目。我可以访问主项目和子项目的源代码。

该子项目使用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.

Test Project

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

疧_╮線 2024-09-23 07:53:56

事实上,您指出至少最终丢失的一件事是 NSString 的类别方法,这让我认为您遇到了一个可爱的问题,这似乎是链接器中可能存在的错误。

首先,请参阅其他地方的答案并确保包括您的库的项目正在使用 -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 文件中。

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.

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

Then the category .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

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.

木緿 2024-09-23 07:53:56

您可以执行 [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.

赠意 2024-09-23 07:53:56

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

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

不是我写过的最漂亮的代码,也不是我以前尝试过的,但也许可以使用运行时函数(如 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:

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

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.

无所谓啦 2024-09-23 07:53:56

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

编辑:意思是,子项目可以设置为针对 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?

咽泪装欢 2024-09-23 07:53:56

也许您应该将条件设置为

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

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

Maybe you should make the conditional into

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

After all, the rest of the referenced project is linked in, correct? The problem must be the preprocessor's conditional.

不醒的梦 2024-09-23 07:53:56

我相信我最近发现了类似的情况,正如我在 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

无言温柔 2024-09-23 07:53:56

答案分为两部分:

  • CoreText.h 导入到应用程序的 PCH 文件中,但不在库中(PCH 不级联)。因此,在库中,__CORETEXT__ 代码始终被#ifdef 剔除。但是,当在应用程序中编译从库导出的标头时,会包含 #ifdef方法原型,因此您不会得到 NSString 上该方法的编译时警告。

    对此的快速解决方法是放置一个文字

    #import ;
    

    在您图书馆的 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

    #import <CoreText/CoreText.h>
    

    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.

    #ifdef WHEE
         // compile-time error will happen here
         #error
    #endif
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文