静态库中的Objective-C类。

时间:2022-09-06 20:10:29

Can you guide me how to properly link static library to iPhone project. I use static library project added to app project as direct dependency (target -> general -> direct dependencies) and all works OK, but categories. A category defined in static library is not working in app.

您能指导我如何正确地将静态库链接到iPhone项目吗?我使用静态库项目作为直接依赖项添加到app项目中(目标-> general ->直接依赖项),所有工作都可以,但类别。静态库中定义的类别不在app中工作。

So my question is how to add static library with some categories into other project?

所以我的问题是如何在其他项目中添加一些类别的静态库?

And in general, what is best practice to use in app project code from other projects?

一般来说,在应用程序项目代码中使用其他项目的最佳实践是什么?

6 个解决方案

#1


218  

Solution: As of Xcode 4.2, you only need to go to the application that is linking against the library (not the library itself) and click the project in the Project Navigator, click your app's target, then build settings, then search for "Other Linker Flags", click the + button, and add '-ObjC'. '-all_load' and '-force_load' are no longer needed.

解决方案:Xcode 4.2,你只需要去对图书馆的应用程序链接(不是图书馆本身),然后单击项目在项目导航器中,单击应用程序的目标,然后构建设置,然后搜索“其他链接器的旗帜”,单击+按钮,并添加“objc”。“-all_load”和“-force_load”不再需要。

Details: I found some answers on various forums, blogs and apple docs. Now I try make short summary of my searches and experiments.

细节:我在各种论坛、博客和苹果文档中找到了答案。现在我试着对我的搜索和实验做一个简短的总结。

Problem was caused by (citation from apple Technical Q&A QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html):

问题是由(苹果技术Q&A QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html)引起的:

Objective-C does not define linker symbols for each function (or method, in Objective-C) - instead, linker symbols are only generated for each class. If you extend a pre-existing class with categories, the linker does not know to associate the object code of the core class implementation and the category implementation. This prevents objects created in the resulting application from responding to a selector that is defined in the category.

Objective-C不为每个函数(或Objective-C中的方法)定义链接器符号,而是为每个类生成链接器符号。如果您将已存在的类扩展为类别,则链接器不知道将核心类实现的对象代码和类别实现关联起来。这可以防止在结果应用程序中创建的对象响应在类别中定义的选择器。

And their solution:

和他们的解决方案:

To resolve this issue, the static library should pass the -ObjC option to the linker. This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.

为了解决这个问题,静态库应该将-ObjC选项传递给链接器。这个标志使链接器在定义Objective-C类或类别的库中加载每个对象文件。虽然此选项通常会导致更大的可执行文件(由于加载到应用程序中的附加对象代码),它将允许成功创建有效的Objective-C静态库,其中包含现有类的类别。

and there is also recommendation in iPhone Development FAQ:

在iPhone开发FAQ中也有建议:

How do I link all the Objective-C classes in a static library? Set the Other Linker Flags build setting to -ObjC.

如何将静态库中的所有Objective-C类链接起来?将其他链接器标志设置为-ObjC。

and flags descriptions:

和标志的描述:

-all_load Loads all members of static archive libraries.

-all_load加载静态存档库的所有成员。

-ObjC Loads all members of static archive libraries that implement an Objective-C class or category.

-ObjC加载实现Objective-C类或类的静态归档库的所有成员。

-force_load (path_to_archive) Loads all members of the specified static archive library. Note: -all_load forces all members of all archives to be loaded. This option allows you to target a specific archive.

-force_load (path_to_archive)加载指定的静态存档库的所有成员。注意:-all_load强制加载所有档案的所有成员。此选项允许您针对特定的存档。

*we can use force_load to reduce app binary size and to avoid conflicts wich all_load can cause in some cases.

*我们可以使用force_load来减少应用程序的二进制大小,避免在某些情况下发生冲突。

Yes, it works with *.a files added to the project. Yet I had troubles with lib project added as direct dependency. But later I found that it was my fault - direct dependency projecct possibly was not added properly. When I remove it and add again with steps:

是的,它适用于*。添加到项目中的文件。然而,我在lib项目中遇到了麻烦,因为它是直接依赖的。但后来我发现这是我的错误——直接依赖项目可能没有被正确地添加。当我把它移开,再加上步骤:

  1. Drag&drop lib project file in app project (or add it with Project->Add to project…).
  2. 在app项目中拖放lib项目文件(或者添加项目->添加到项目…)。
  3. Click on arrow at lib project icon - mylib.a file name shown, drag this mylib.a file and drop it into Target -> Link Binary With Library group.
  4. 单击lib项目图标- mylib中的箭头。显示的文件名,拖动此mylib。一个文件并将它放入目标->链接二进制与库组。
  5. Open target info in fist page (General) and add my lib to dependencies list
  6. 在第一页(General)中打开目标信息,并将我的lib添加到依赖项列表中。

after that all works OK. "-ObjC" flag was enough in my case.

之后一切正常。在我的情况下,objc标志就足够了。

I also was interested with idea from http://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.html blog. Author say he can use category from lib without setting -all_load or -ObjC flag. He just add to category h/m files empty dummy class interface/implementation to force linker use this file. And yes, this trick do the job.

我也对http://iphonedevelopmentexperiences.blogspot.com/2010/03/category -static-library.html博客感兴趣。作者说,他可以在没有设置-all_load或-ObjC标志的情况下从lib中使用类别。他只是添加到类别h/m文件,空的虚拟类接口/实现来强制链接器使用这个文件。是的,这个技巧能起到作用。

But author also said he even not instantiated dummy object. Mm… As I've found we should explicitly call some "real" code from category file. So at least class function should be called. And we even need not dummy class. Single c function do the same.

但作者也表示,他甚至没有实例化虚拟对象。我发现我们应该明确地从类别文件中调用一些“真正的”代码。所以至少应该调用类函数。我们甚至不需要虚拟的类。单个c函数也一样。

So if we write lib files as:

如果我们把lib文件写成:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

and if we call useMyLib(); anywhere in App project then in any class we can use logSelf category method;

如果我们调用useMyLib();在App项目中的任何地方我们都可以使用logSelf分类方法;

[self logSelf];

And more blogs on theme:

更多关于主题的博客:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

#2


106  

The answer from Vladimir is actually pretty good, however, I'd like to give some more background knowledge here. Maybe one day somebody finds my reply and may find it helpful.

弗拉基米尔的回答其实很好,不过,我想在这里多介绍一些背景知识。也许有一天会有人找到我的答案,也许会发现它很有帮助。

The compiler transforms source files (.c, .cc, .cpp, .m) into object files (.o). There is one object file per source file. Object files contain symbols, code and data. Object files are not usable directly by the operating system.

编译器会转换源文件(。c, .cc, .cpp, .m)到对象文件(.o)。每个源文件有一个对象文件。对象文件包含符号、代码和数据。对象文件不能直接使用操作系统。

Now when building a dynamic library (.dylib), a framework, a loadable bundle (.bundle) or an executable binary, these object files are linked together by the linker to produce something the operating system considers "usable", e.g. something it can directly load to a specific memory address.

现在,当构建一个动态库(.dylib)、一个框架、一个可加载的bundle (.bundle)或一个可执行的二进制文件时,这些对象文件由链接器连接在一起,以生成操作系统认为“可用”的东西,例如它可以直接加载到特定的内存地址。

However when building a static library, all these object files are simply added to a big archive file, hence the extension of static libraries (.a for archive). So an .a file is nothing than an archive of object (.o) files. Think of a TAR archive or a ZIP archive without compression. It's just easier to copy a single .a file around than a whole bunch of .o files (similar to Java, where you pack .class files into a .jar archive for easy distribution).

但是,当构建一个静态库时,所有这些对象文件都被简单地添加到一个大的存档文件中,从而扩展了静态库(。一个存档)。因此,一个文件就是一个对象(.o)文件的归档文件。想一下TAR存档文件或ZIP归档文件,而不需要压缩。复制一个文件比复制一大堆.o文件(类似于Java,将.class文件打包到.jar归档文件中以方便分发)要简单得多。

When linking a binary to a static library (= archive), the linker will get a table of all symbols in the archive and check which of these symbols are referenced by the binaries. Only the object files containing referenced symbols are actually loaded by the linker and are considered by the linking process. E.g. if your archive has 50 object files, but only 20 contain symbols used by the binary, only those 20 are loaded by the linker, the other 30 are entirely ignored in the linking process.

当将二进制文件链接到静态库(= archive)时,链接器将得到存档中所有符号的表,并检查二进制文件引用了哪些符号。只有包含引用符号的对象文件实际上是由链接器加载的,并由链接过程考虑。如果您的存档有50个对象文件,但是只有20个包含二进制文件,只有20个是由链接器加载的,其余30个在链接过程中完全被忽略。

This works quite well for C and C++ code, as these languages try to do as much as possible at compile time (though C++ also has some runtime-only features). Obj-C, however, is a different kind of language. Obj-C heavily depends on runtime features and many Obj-C features are actually runtime-only features. Obj-C classes actually have symbols comparable to C functions or global C variables (at least in current Obj-C runtime). A linker can see if a class is referenced or not, so it can determine a class being in use or not. If you use a class from an object file in a static library, this object file will be loaded by the linker because the linker sees a symbol being in use. Categories are a runtime-only feature, categories aren't symbols like classes or functions and that also means a linker cannot determine if a category is in use or not.

这对于C和c++代码非常有效,因为这些语言在编译时尽量多做(尽管c++也有一些运行时特性)。然而,objc - c是一种不同的语言。objc - c很大程度上依赖于运行时特性,而许多objc - c特性实际上只是运行时的特性。objc类实际上有与C函数或全局C变量相比较的符号(至少在当前objc -C运行时)。链接器可以看到一个类是否被引用,因此它可以确定一个正在使用的类。如果您在静态库中使用一个对象文件中的类,那么这个对象文件将被链接器加载,因为链接器看到了正在使用的符号。类别是一个运行时的特性,类别不是类或函数的符号,这也意味着链接器不能确定类别是否在使用。

If the linker loads an object file containing Obj-C code, all Obj-C parts of it are always part of the linking stage. So if an object file containing categories is loaded because any symbol from it is considered "in use" (be it a class, be it a function, be it a global variable), the categories are loaded as well and will be available at runtime. Yet if the object file itself is not loaded, the categories in it will not be available at runtime. An object file containing only categories is never loaded because it contains no symbols the linker would ever consider "in use". And this is the whole problem here.

如果链接器加载一个包含objc代码的对象文件,那么它的所有objc部分都是链接阶段的一部分。因此,如果一个包含类别的对象文件被加载,因为它的任何符号都被认为是“在使用”(它是一个类,它是一个函数,是一个全局变量),类别也被加载,并且在运行时将会得到。但是,如果对象文件本身没有加载,那么它的类别在运行时将无法使用。一个只包含类别的对象文件永远不会被加载,因为它不包含链接器会考虑“使用”的任何符号。这就是整个问题。

Several solutions have been proposed and now that you know how all this plays together, let's have another look on the proposed solution:

已经提出了几个解决方案,现在您知道了所有这些是如何组合在一起的,让我们再看一下建议的解决方案:

  1. One solution is to add -all_load to the linker call. What will that linker flag actually do? Actually it tells the linker the following "Load all object files of all archives regardless if you see any symbol in use or not'. Of course, that will work; but it may also produce rather big binaries.

    一个解决方案是向链接器调用添加-all_load。那个链接器会做什么呢?实际上,它告诉链接器“无论您是否看到任何使用的符号,都要加载所有档案的所有对象文件”。当然,这是行得通的;但它也可能产生相当大的双星。

  2. Another solution is to add -force_load to the linker call including the path to the archive. This flag works exactly like -all_load, but only for the specified archive. Of course this will work as well.

    另一种解决方案是将-force_load添加到链接器调用中,包括归档文件的路径。此标记与-all_load完全相同,但仅适用于指定的存档。当然这也会起作用。

  3. The most popular solution is to add -ObjC to the linker call. What will that linker flag actually do? This flag tells the linker "Load all object files from all archives if you see that they contain any Obj-C code". And "any Obj-C code" includes categories. This will work as well and it will not force loading of object files containing no Obj-C code (these are still only loaded on demand).

    最流行的解决方案是将-ObjC添加到链接器调用中。那个链接器会做什么呢?这个标志告诉链接器“如果您看到它们包含任何objc代码,就从所有的档案中加载所有的对象文件”。“任何objc代码”包括类别。这也会起作用,它不会强制加载不包含objc代码的对象文件(这些文件仍然只按需加载)。

  4. Another solution is the rather new Xcode build setting Perform Single-Object Prelink. What will this setting do? If enabled, all the object files (remember, there is one per source file) are merged together into a single object file (that is not real linking, hence the name PreLink) and this single object file (sometimes also called a "master object file") is then added to the archive. If now any symbol of the master object file is considered in use, the whole master object file is considered in use and thus all Objective-C parts of it are always loaded. And since classes are normal symbols, it's enough to use a single class from such a static library to also get all the categories.

    另一个解决方案是新的Xcode构建设置执行单对象预链接。这个设置会做什么?如果启用,所有的对象文件(记住,每个源文件都有一个)被合并到一个单独的对象文件中(这不是真正的链接,因此命名为PreLink),然后将这个单一对象文件(有时也称为“主对象文件”)添加到归档中。如果在使用中考虑了主对象文件的任何符号,就会考虑使用整个主对象文件,因此所有的Objective-C部分都将被加载。由于类是正常的符号,所以从这样的静态库中使用一个类也就足够了。

  5. The final solution is the trick Vladimir added at the very end of his answer. Place a "fake symbol" into any source file declaring only categories. If you want to use any of the categories at runtime, make sure you somehow reference the fake symbol at compile time, as this causes the object file to be loaded by the linker and thus also all Obj-C code in it. E.g. it could be a function with an empty function body (which will do nothing when being called) or it could be a global variable accessed (e.g. a global int once read or once written, this is sufficient). Unlike all other solutions above, this solution shifts control about which categories are available at runtime to the compiled code (if it wants them to be linked and available, it accesses the symbol, otherwise it doesn't access the symbol and the linker will ignore it).

    最终的解决方案是弗拉基米尔在他的回答最后加了一个诡计。将一个“假符号”放入任何只声明类别的源文件中。如果您想在运行时使用任何类别,一定要在编译时引用伪符号,因为这会导致链接器加载对象文件,因此也会在其中包含所有objc代码。例如,它可以是一个具有空函数体(当被调用时什么都不做)的函数,或者它可以是一个被访问的全局变量(例如,一个全局变量,一旦读过或写过,这就足够了)。与上面的所有其他解决方案不同,此解决方案将运行时对哪些类别可用的控制转移到已编译的代码(如果它希望它们被链接和可用,它访问符号,否则它不访问符号,链接器将忽略它)。

That's all folks.

这是所有的人。

Oh, wait, there's one more thing:
The linker has an option named -dead_strip. What does this option do? If the linker decided to load an object file, all symbols of the object file become part of the linked binary, whether they are used or not. E.g. an object file contains 100 functions, but only one of them is used by the binary, all 100 functions are still added to the binary because object files are either added as a whole or they are not added at all. Adding an object file partially is usually not supported by linkers.

哦,等等,还有一件事:链接器有一个名为-dead_strip的选项。这个选项是做什么的?如果链接器决定加载一个对象文件,那么无论使用与否,对象文件的所有符号都成为链接二进制文件的一部分。例如,一个对象文件包含100个函数,但只有其中一个被二进制文件使用,所有100个函数仍然被添加到二进制文件中,因为对象文件要么作为一个整体添加,要么完全没有添加。添加对象文件部分通常不受链接器的支持。

However, if you tell the linker to "dead strip", the linker will first add all the object files to the binary, resolve all the references and finally scan the binary for symbols not in use (or only in use by other symbols not in use). All the symbols found to be not in use are then removed as part of the optimization stage. In the example above, the 99 unused functions are removed again. This is very useful if you use options like -load_all, -force_load or Perform Single-Object Prelink because these options can easily blow up binary sizes dramatically in some cases and the dead stripping will remove unused code and data again.

但是,如果您将链接器告知“死条”,链接器将首先将所有的对象文件添加到二进制文件中,解析所有的引用,最后扫描二进制文件,以获得不使用的符号(或仅在其他不使用的符号中使用)。所有未使用的符号将被移除作为优化阶段的一部分。在上面的示例中,再次删除了99个未使用的函数。如果您使用诸如-load_all、-force_load或执行单对象预链接之类的选项,这非常有用,因为这些选项在某些情况下可以很容易地放大二进制大小,而死剥离将再次删除未使用的代码和数据。

Dead stripping works very well for C code (e.g. unused functions, variables and constants are removed as expected) and it also works quite good for C++ (e.g. unused classes are removed). It is not perfect, in some cases some symbols are not removed even though it would be okay to remove them, but in most cases it works quite well for these languages.

对于C代码(如未使用的函数、变量和常量,如预期的那样被删除),死剥离工作非常好,而且对于c++(例如,未使用的类被删除)也非常有用。它不是完美的,在某些情况下,一些符号并没有被删除,即使删除它们是可以的,但是在大多数情况下,它对这些语言来说是很好的。

What about Obj-C? Forget about it! There is no dead stripping for Obj-C. As Obj-C is a runtime-feature language, the compiler cannot say at compile time whether a symbol is really in use or not. E.g. an Obj-C class is not in use if there is no code directly referencing it, correct? Wrong! You can dynamically build a string containing a class name, request a class pointer for that name and dynamically allocate the class. E.g. instead of

Obj-C呢?忘记它!对于objc,没有死法。由于object - c是一个运行时特性的语言,编译器在编译时不能说符号是否真的在使用。如果没有直接引用的代码,则不使用objc - c类,对吗?错了!您可以动态地构建一个包含类名的字符串,为该名称请求一个类指针,并动态地分配类。例如,而不是

MyCoolClass * mcc = [[MyCoolClass alloc] init];

I would also write

我也会写

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

In both cases mmc is a reference to an object of the class "MyCoolClass", but there is no direct reference to this class in the second code sample (not even the class name as a static string). Everything happens only at runtime. And that's even though classes are actually real symbols. It's even worse for categories, as they are not even real symbols.

在这两种情况下,mmc都是对类“MyCoolClass”对象的引用,但是在第二个代码示例中没有直接引用这个类(甚至不作为静态字符串的类名)。一切只在运行时发生。这是即使类实际上是真正的符号。这对分类来说更糟,因为它们甚至不是真正的符号。

So if you have a static library with hundreds of objects, yet most of your binaries only need a few of them, you may prefer not to use the solutions (1) to (4) above. Otherwise you end up with very big binaries containing all these classes, even though most of them are never used. For classes you usually don't need any special solution at all since classes have real symbols and as long as you reference them directly (not as in the second code sample), the linker will identify their usage pretty well on its own. For categories, though, consider solution (5), as it makes it possible to only include the categories you really need.

因此,如果您有一个具有数百个对象的静态库,但是大多数二进制文件只需要其中的几个,您可能不愿意使用上面的解决方案(1)到(4)。否则,您就会得到包含所有这些类的非常大的二进制文件,即使它们中的大多数从未使用过。对于类,您通常不需要任何特殊的解决方案,因为类具有真正的符号,只要您直接引用它们(而不是在第二个代码示例中),链接器就会非常清楚地识别它们的用法。但是,对于类别,请考虑解决方案(5),因为它使得只包含您真正需要的类别成为可能。

E.g. if you want a category for NSData, e.g. adding a compression/decompression method to it, you'd create a header file:

如果你想要一个NSData的类别,例如给它添加一个压缩/解压方法,你会创建一个头文件:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

and an implementation file

和一个实现文件

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

Now just make sure that anywhere in your code import_NSData_Compression() is called. It doesn't matter where it is called or how often it is called. Actually it doesn't really have to be called at all, it's enough if the linker thinks so. E.g. you could put the following code anywhere in your project:

现在只要确保代码import_NSData_Compression()中的任何地方都被调用。它在哪里被调用或被调用的频率并不重要。实际上它根本不需要被调用,如果链接器这么想就足够了。你可以在你的项目的任何地方放置以下代码:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

You don't have to ever call importCategories() in your code, the attribute will make the compiler and linker believe that it is called, even in case it is not.

您不必在代码中调用importCategories(),该属性将使编译器和链接器相信它是被调用的,即使它不是。

And a final tip:
If you add -whyload to the final link call, the linker will print in the build log which object file from which library it did load because of which symbol in use. It will only print the first symbol considered in use, but that is not necessarily the only symbol in use of that object file.

最后一个提示:如果将-whyload添加到最终的链接调用中,链接器将在构建日志中打印它所加载的库的对象文件,因为它使用的是哪个符号。它只会打印在使用中考虑的第一个符号,但这并不一定是使用该对象文件的惟一符号。

#3


24  

This issue has been fixed in LLVM. The fix ships as part of LLVM 2.9 The first Xcode version to contain the fix is Xcode 4.2 shipping with LLVM 3.0. The usage of -all_load or -force_load is no longer needed when working with XCode 4.2 -ObjC is still needed.

这个问题已经在LLVM中解决了。修复船作为LLVM 2.9的一部分,第一个包含修复的Xcode版本是Xcode 4.2,附带LLVM 3.0。在使用XCode 4.2 -ObjC时,使用-all_load或-force_load不再需要。

#4


16  

Here's what you need to do to resolve this problem completely when compiling your static library:

在编译静态库时,您需要做的是完全解决这个问题:

Either go to Xcode Build Settings and set Perform Single-Object Prelink to YES or GENERATE_MASTER_OBJECT_FILE = YES in your build configuration file.

要么转到Xcode构建设置,要么在构建配置文件中设置为YES或GENERATE_MASTER_OBJECT_FILE = YES的单对象预链接。

By default,the linker generates an .o file for each .m file. So categories gets different .o files. When the linker looks at a static library .o files, it doesn't create an index of all symbols per class (Runtime will, doesn't matter what).

默认情况下,链接器为每个.m文件生成一个.o文件。所以类别得到了不同的。o文件。当链接器查看静态库.o文件时,它不会为每个类创建一个所有符号的索引(运行时将会发生什么)。

This directive will ask the linker to pack all objects together into one big .o file and by this it forces the linker that process the static library to get index all class categories.

这个指令会要求链接器将所有对象打包成一个大的.o文件,并通过这个文件强制连接静态库的链接器获得所有类类别的索引。

Hope that clarifies it.

希望澄清。

#5


9  

One factor that is rarely mentioned whenever the static library linking discussion comes up is the fact that you must also include the categories themselves in the build phases->copy files and compile sources of the static library itself.

在静态库链接讨论时,很少提到的一个因素是,您还必须在构建阶段中包含这些类别—b0拷贝文件和编译静态库本身的源。

Apple also doesn't emphasize this fact in their recently published Using Static Libraries in iOS either.

苹果也没有在最近发布的iOS中使用静态库强调这一事实。

I spent a whole day trying all sorts of variations of -objC and -all_load etc.. but nothing came out of it.. this question brought that issue to my attention. (don't get me wrong.. you still have to do the -objC stuff.. but it's more than just that).

我花了一整天的时间尝试各种各样的-objC和-all_load等等。但是什么也没有发生。这个问题引起了我的注意。(别误会我. .你仍然需要做-objC。但它不仅仅是这样的。

also another action that has always helped me is that I always build the included static library first on its own.. then i build the enclosing application..

另外一个一直帮助我的操作是,我总是首先自己构建包含的静态库。然后构建封闭应用程序。

#6


-1  

You probably need to have the category in you're static library's "public" header: #import "MyStaticLib.h"

您可能需要在静态库的“公共”标题:#import“MyStaticLib.h”中设置类别

#1


218  

Solution: As of Xcode 4.2, you only need to go to the application that is linking against the library (not the library itself) and click the project in the Project Navigator, click your app's target, then build settings, then search for "Other Linker Flags", click the + button, and add '-ObjC'. '-all_load' and '-force_load' are no longer needed.

解决方案:Xcode 4.2,你只需要去对图书馆的应用程序链接(不是图书馆本身),然后单击项目在项目导航器中,单击应用程序的目标,然后构建设置,然后搜索“其他链接器的旗帜”,单击+按钮,并添加“objc”。“-all_load”和“-force_load”不再需要。

Details: I found some answers on various forums, blogs and apple docs. Now I try make short summary of my searches and experiments.

细节:我在各种论坛、博客和苹果文档中找到了答案。现在我试着对我的搜索和实验做一个简短的总结。

Problem was caused by (citation from apple Technical Q&A QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html):

问题是由(苹果技术Q&A QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html)引起的:

Objective-C does not define linker symbols for each function (or method, in Objective-C) - instead, linker symbols are only generated for each class. If you extend a pre-existing class with categories, the linker does not know to associate the object code of the core class implementation and the category implementation. This prevents objects created in the resulting application from responding to a selector that is defined in the category.

Objective-C不为每个函数(或Objective-C中的方法)定义链接器符号,而是为每个类生成链接器符号。如果您将已存在的类扩展为类别,则链接器不知道将核心类实现的对象代码和类别实现关联起来。这可以防止在结果应用程序中创建的对象响应在类别中定义的选择器。

And their solution:

和他们的解决方案:

To resolve this issue, the static library should pass the -ObjC option to the linker. This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.

为了解决这个问题,静态库应该将-ObjC选项传递给链接器。这个标志使链接器在定义Objective-C类或类别的库中加载每个对象文件。虽然此选项通常会导致更大的可执行文件(由于加载到应用程序中的附加对象代码),它将允许成功创建有效的Objective-C静态库,其中包含现有类的类别。

and there is also recommendation in iPhone Development FAQ:

在iPhone开发FAQ中也有建议:

How do I link all the Objective-C classes in a static library? Set the Other Linker Flags build setting to -ObjC.

如何将静态库中的所有Objective-C类链接起来?将其他链接器标志设置为-ObjC。

and flags descriptions:

和标志的描述:

-all_load Loads all members of static archive libraries.

-all_load加载静态存档库的所有成员。

-ObjC Loads all members of static archive libraries that implement an Objective-C class or category.

-ObjC加载实现Objective-C类或类的静态归档库的所有成员。

-force_load (path_to_archive) Loads all members of the specified static archive library. Note: -all_load forces all members of all archives to be loaded. This option allows you to target a specific archive.

-force_load (path_to_archive)加载指定的静态存档库的所有成员。注意:-all_load强制加载所有档案的所有成员。此选项允许您针对特定的存档。

*we can use force_load to reduce app binary size and to avoid conflicts wich all_load can cause in some cases.

*我们可以使用force_load来减少应用程序的二进制大小,避免在某些情况下发生冲突。

Yes, it works with *.a files added to the project. Yet I had troubles with lib project added as direct dependency. But later I found that it was my fault - direct dependency projecct possibly was not added properly. When I remove it and add again with steps:

是的,它适用于*。添加到项目中的文件。然而,我在lib项目中遇到了麻烦,因为它是直接依赖的。但后来我发现这是我的错误——直接依赖项目可能没有被正确地添加。当我把它移开,再加上步骤:

  1. Drag&drop lib project file in app project (or add it with Project->Add to project…).
  2. 在app项目中拖放lib项目文件(或者添加项目->添加到项目…)。
  3. Click on arrow at lib project icon - mylib.a file name shown, drag this mylib.a file and drop it into Target -> Link Binary With Library group.
  4. 单击lib项目图标- mylib中的箭头。显示的文件名,拖动此mylib。一个文件并将它放入目标->链接二进制与库组。
  5. Open target info in fist page (General) and add my lib to dependencies list
  6. 在第一页(General)中打开目标信息,并将我的lib添加到依赖项列表中。

after that all works OK. "-ObjC" flag was enough in my case.

之后一切正常。在我的情况下,objc标志就足够了。

I also was interested with idea from http://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.html blog. Author say he can use category from lib without setting -all_load or -ObjC flag. He just add to category h/m files empty dummy class interface/implementation to force linker use this file. And yes, this trick do the job.

我也对http://iphonedevelopmentexperiences.blogspot.com/2010/03/category -static-library.html博客感兴趣。作者说,他可以在没有设置-all_load或-ObjC标志的情况下从lib中使用类别。他只是添加到类别h/m文件,空的虚拟类接口/实现来强制链接器使用这个文件。是的,这个技巧能起到作用。

But author also said he even not instantiated dummy object. Mm… As I've found we should explicitly call some "real" code from category file. So at least class function should be called. And we even need not dummy class. Single c function do the same.

但作者也表示,他甚至没有实例化虚拟对象。我发现我们应该明确地从类别文件中调用一些“真正的”代码。所以至少应该调用类函数。我们甚至不需要虚拟的类。单个c函数也一样。

So if we write lib files as:

如果我们把lib文件写成:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

and if we call useMyLib(); anywhere in App project then in any class we can use logSelf category method;

如果我们调用useMyLib();在App项目中的任何地方我们都可以使用logSelf分类方法;

[self logSelf];

And more blogs on theme:

更多关于主题的博客:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html

#2


106  

The answer from Vladimir is actually pretty good, however, I'd like to give some more background knowledge here. Maybe one day somebody finds my reply and may find it helpful.

弗拉基米尔的回答其实很好,不过,我想在这里多介绍一些背景知识。也许有一天会有人找到我的答案,也许会发现它很有帮助。

The compiler transforms source files (.c, .cc, .cpp, .m) into object files (.o). There is one object file per source file. Object files contain symbols, code and data. Object files are not usable directly by the operating system.

编译器会转换源文件(。c, .cc, .cpp, .m)到对象文件(.o)。每个源文件有一个对象文件。对象文件包含符号、代码和数据。对象文件不能直接使用操作系统。

Now when building a dynamic library (.dylib), a framework, a loadable bundle (.bundle) or an executable binary, these object files are linked together by the linker to produce something the operating system considers "usable", e.g. something it can directly load to a specific memory address.

现在,当构建一个动态库(.dylib)、一个框架、一个可加载的bundle (.bundle)或一个可执行的二进制文件时,这些对象文件由链接器连接在一起,以生成操作系统认为“可用”的东西,例如它可以直接加载到特定的内存地址。

However when building a static library, all these object files are simply added to a big archive file, hence the extension of static libraries (.a for archive). So an .a file is nothing than an archive of object (.o) files. Think of a TAR archive or a ZIP archive without compression. It's just easier to copy a single .a file around than a whole bunch of .o files (similar to Java, where you pack .class files into a .jar archive for easy distribution).

但是,当构建一个静态库时,所有这些对象文件都被简单地添加到一个大的存档文件中,从而扩展了静态库(。一个存档)。因此,一个文件就是一个对象(.o)文件的归档文件。想一下TAR存档文件或ZIP归档文件,而不需要压缩。复制一个文件比复制一大堆.o文件(类似于Java,将.class文件打包到.jar归档文件中以方便分发)要简单得多。

When linking a binary to a static library (= archive), the linker will get a table of all symbols in the archive and check which of these symbols are referenced by the binaries. Only the object files containing referenced symbols are actually loaded by the linker and are considered by the linking process. E.g. if your archive has 50 object files, but only 20 contain symbols used by the binary, only those 20 are loaded by the linker, the other 30 are entirely ignored in the linking process.

当将二进制文件链接到静态库(= archive)时,链接器将得到存档中所有符号的表,并检查二进制文件引用了哪些符号。只有包含引用符号的对象文件实际上是由链接器加载的,并由链接过程考虑。如果您的存档有50个对象文件,但是只有20个包含二进制文件,只有20个是由链接器加载的,其余30个在链接过程中完全被忽略。

This works quite well for C and C++ code, as these languages try to do as much as possible at compile time (though C++ also has some runtime-only features). Obj-C, however, is a different kind of language. Obj-C heavily depends on runtime features and many Obj-C features are actually runtime-only features. Obj-C classes actually have symbols comparable to C functions or global C variables (at least in current Obj-C runtime). A linker can see if a class is referenced or not, so it can determine a class being in use or not. If you use a class from an object file in a static library, this object file will be loaded by the linker because the linker sees a symbol being in use. Categories are a runtime-only feature, categories aren't symbols like classes or functions and that also means a linker cannot determine if a category is in use or not.

这对于C和c++代码非常有效,因为这些语言在编译时尽量多做(尽管c++也有一些运行时特性)。然而,objc - c是一种不同的语言。objc - c很大程度上依赖于运行时特性,而许多objc - c特性实际上只是运行时的特性。objc类实际上有与C函数或全局C变量相比较的符号(至少在当前objc -C运行时)。链接器可以看到一个类是否被引用,因此它可以确定一个正在使用的类。如果您在静态库中使用一个对象文件中的类,那么这个对象文件将被链接器加载,因为链接器看到了正在使用的符号。类别是一个运行时的特性,类别不是类或函数的符号,这也意味着链接器不能确定类别是否在使用。

If the linker loads an object file containing Obj-C code, all Obj-C parts of it are always part of the linking stage. So if an object file containing categories is loaded because any symbol from it is considered "in use" (be it a class, be it a function, be it a global variable), the categories are loaded as well and will be available at runtime. Yet if the object file itself is not loaded, the categories in it will not be available at runtime. An object file containing only categories is never loaded because it contains no symbols the linker would ever consider "in use". And this is the whole problem here.

如果链接器加载一个包含objc代码的对象文件,那么它的所有objc部分都是链接阶段的一部分。因此,如果一个包含类别的对象文件被加载,因为它的任何符号都被认为是“在使用”(它是一个类,它是一个函数,是一个全局变量),类别也被加载,并且在运行时将会得到。但是,如果对象文件本身没有加载,那么它的类别在运行时将无法使用。一个只包含类别的对象文件永远不会被加载,因为它不包含链接器会考虑“使用”的任何符号。这就是整个问题。

Several solutions have been proposed and now that you know how all this plays together, let's have another look on the proposed solution:

已经提出了几个解决方案,现在您知道了所有这些是如何组合在一起的,让我们再看一下建议的解决方案:

  1. One solution is to add -all_load to the linker call. What will that linker flag actually do? Actually it tells the linker the following "Load all object files of all archives regardless if you see any symbol in use or not'. Of course, that will work; but it may also produce rather big binaries.

    一个解决方案是向链接器调用添加-all_load。那个链接器会做什么呢?实际上,它告诉链接器“无论您是否看到任何使用的符号,都要加载所有档案的所有对象文件”。当然,这是行得通的;但它也可能产生相当大的双星。

  2. Another solution is to add -force_load to the linker call including the path to the archive. This flag works exactly like -all_load, but only for the specified archive. Of course this will work as well.

    另一种解决方案是将-force_load添加到链接器调用中,包括归档文件的路径。此标记与-all_load完全相同,但仅适用于指定的存档。当然这也会起作用。

  3. The most popular solution is to add -ObjC to the linker call. What will that linker flag actually do? This flag tells the linker "Load all object files from all archives if you see that they contain any Obj-C code". And "any Obj-C code" includes categories. This will work as well and it will not force loading of object files containing no Obj-C code (these are still only loaded on demand).

    最流行的解决方案是将-ObjC添加到链接器调用中。那个链接器会做什么呢?这个标志告诉链接器“如果您看到它们包含任何objc代码,就从所有的档案中加载所有的对象文件”。“任何objc代码”包括类别。这也会起作用,它不会强制加载不包含objc代码的对象文件(这些文件仍然只按需加载)。

  4. Another solution is the rather new Xcode build setting Perform Single-Object Prelink. What will this setting do? If enabled, all the object files (remember, there is one per source file) are merged together into a single object file (that is not real linking, hence the name PreLink) and this single object file (sometimes also called a "master object file") is then added to the archive. If now any symbol of the master object file is considered in use, the whole master object file is considered in use and thus all Objective-C parts of it are always loaded. And since classes are normal symbols, it's enough to use a single class from such a static library to also get all the categories.

    另一个解决方案是新的Xcode构建设置执行单对象预链接。这个设置会做什么?如果启用,所有的对象文件(记住,每个源文件都有一个)被合并到一个单独的对象文件中(这不是真正的链接,因此命名为PreLink),然后将这个单一对象文件(有时也称为“主对象文件”)添加到归档中。如果在使用中考虑了主对象文件的任何符号,就会考虑使用整个主对象文件,因此所有的Objective-C部分都将被加载。由于类是正常的符号,所以从这样的静态库中使用一个类也就足够了。

  5. The final solution is the trick Vladimir added at the very end of his answer. Place a "fake symbol" into any source file declaring only categories. If you want to use any of the categories at runtime, make sure you somehow reference the fake symbol at compile time, as this causes the object file to be loaded by the linker and thus also all Obj-C code in it. E.g. it could be a function with an empty function body (which will do nothing when being called) or it could be a global variable accessed (e.g. a global int once read or once written, this is sufficient). Unlike all other solutions above, this solution shifts control about which categories are available at runtime to the compiled code (if it wants them to be linked and available, it accesses the symbol, otherwise it doesn't access the symbol and the linker will ignore it).

    最终的解决方案是弗拉基米尔在他的回答最后加了一个诡计。将一个“假符号”放入任何只声明类别的源文件中。如果您想在运行时使用任何类别,一定要在编译时引用伪符号,因为这会导致链接器加载对象文件,因此也会在其中包含所有objc代码。例如,它可以是一个具有空函数体(当被调用时什么都不做)的函数,或者它可以是一个被访问的全局变量(例如,一个全局变量,一旦读过或写过,这就足够了)。与上面的所有其他解决方案不同,此解决方案将运行时对哪些类别可用的控制转移到已编译的代码(如果它希望它们被链接和可用,它访问符号,否则它不访问符号,链接器将忽略它)。

That's all folks.

这是所有的人。

Oh, wait, there's one more thing:
The linker has an option named -dead_strip. What does this option do? If the linker decided to load an object file, all symbols of the object file become part of the linked binary, whether they are used or not. E.g. an object file contains 100 functions, but only one of them is used by the binary, all 100 functions are still added to the binary because object files are either added as a whole or they are not added at all. Adding an object file partially is usually not supported by linkers.

哦,等等,还有一件事:链接器有一个名为-dead_strip的选项。这个选项是做什么的?如果链接器决定加载一个对象文件,那么无论使用与否,对象文件的所有符号都成为链接二进制文件的一部分。例如,一个对象文件包含100个函数,但只有其中一个被二进制文件使用,所有100个函数仍然被添加到二进制文件中,因为对象文件要么作为一个整体添加,要么完全没有添加。添加对象文件部分通常不受链接器的支持。

However, if you tell the linker to "dead strip", the linker will first add all the object files to the binary, resolve all the references and finally scan the binary for symbols not in use (or only in use by other symbols not in use). All the symbols found to be not in use are then removed as part of the optimization stage. In the example above, the 99 unused functions are removed again. This is very useful if you use options like -load_all, -force_load or Perform Single-Object Prelink because these options can easily blow up binary sizes dramatically in some cases and the dead stripping will remove unused code and data again.

但是,如果您将链接器告知“死条”,链接器将首先将所有的对象文件添加到二进制文件中,解析所有的引用,最后扫描二进制文件,以获得不使用的符号(或仅在其他不使用的符号中使用)。所有未使用的符号将被移除作为优化阶段的一部分。在上面的示例中,再次删除了99个未使用的函数。如果您使用诸如-load_all、-force_load或执行单对象预链接之类的选项,这非常有用,因为这些选项在某些情况下可以很容易地放大二进制大小,而死剥离将再次删除未使用的代码和数据。

Dead stripping works very well for C code (e.g. unused functions, variables and constants are removed as expected) and it also works quite good for C++ (e.g. unused classes are removed). It is not perfect, in some cases some symbols are not removed even though it would be okay to remove them, but in most cases it works quite well for these languages.

对于C代码(如未使用的函数、变量和常量,如预期的那样被删除),死剥离工作非常好,而且对于c++(例如,未使用的类被删除)也非常有用。它不是完美的,在某些情况下,一些符号并没有被删除,即使删除它们是可以的,但是在大多数情况下,它对这些语言来说是很好的。

What about Obj-C? Forget about it! There is no dead stripping for Obj-C. As Obj-C is a runtime-feature language, the compiler cannot say at compile time whether a symbol is really in use or not. E.g. an Obj-C class is not in use if there is no code directly referencing it, correct? Wrong! You can dynamically build a string containing a class name, request a class pointer for that name and dynamically allocate the class. E.g. instead of

Obj-C呢?忘记它!对于objc,没有死法。由于object - c是一个运行时特性的语言,编译器在编译时不能说符号是否真的在使用。如果没有直接引用的代码,则不使用objc - c类,对吗?错了!您可以动态地构建一个包含类名的字符串,为该名称请求一个类指针,并动态地分配类。例如,而不是

MyCoolClass * mcc = [[MyCoolClass alloc] init];

I would also write

我也会写

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

In both cases mmc is a reference to an object of the class "MyCoolClass", but there is no direct reference to this class in the second code sample (not even the class name as a static string). Everything happens only at runtime. And that's even though classes are actually real symbols. It's even worse for categories, as they are not even real symbols.

在这两种情况下,mmc都是对类“MyCoolClass”对象的引用,但是在第二个代码示例中没有直接引用这个类(甚至不作为静态字符串的类名)。一切只在运行时发生。这是即使类实际上是真正的符号。这对分类来说更糟,因为它们甚至不是真正的符号。

So if you have a static library with hundreds of objects, yet most of your binaries only need a few of them, you may prefer not to use the solutions (1) to (4) above. Otherwise you end up with very big binaries containing all these classes, even though most of them are never used. For classes you usually don't need any special solution at all since classes have real symbols and as long as you reference them directly (not as in the second code sample), the linker will identify their usage pretty well on its own. For categories, though, consider solution (5), as it makes it possible to only include the categories you really need.

因此,如果您有一个具有数百个对象的静态库,但是大多数二进制文件只需要其中的几个,您可能不愿意使用上面的解决方案(1)到(4)。否则,您就会得到包含所有这些类的非常大的二进制文件,即使它们中的大多数从未使用过。对于类,您通常不需要任何特殊的解决方案,因为类具有真正的符号,只要您直接引用它们(而不是在第二个代码示例中),链接器就会非常清楚地识别它们的用法。但是,对于类别,请考虑解决方案(5),因为它使得只包含您真正需要的类别成为可能。

E.g. if you want a category for NSData, e.g. adding a compression/decompression method to it, you'd create a header file:

如果你想要一个NSData的类别,例如给它添加一个压缩/解压方法,你会创建一个头文件:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

and an implementation file

和一个实现文件

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

Now just make sure that anywhere in your code import_NSData_Compression() is called. It doesn't matter where it is called or how often it is called. Actually it doesn't really have to be called at all, it's enough if the linker thinks so. E.g. you could put the following code anywhere in your project:

现在只要确保代码import_NSData_Compression()中的任何地方都被调用。它在哪里被调用或被调用的频率并不重要。实际上它根本不需要被调用,如果链接器这么想就足够了。你可以在你的项目的任何地方放置以下代码:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

You don't have to ever call importCategories() in your code, the attribute will make the compiler and linker believe that it is called, even in case it is not.

您不必在代码中调用importCategories(),该属性将使编译器和链接器相信它是被调用的,即使它不是。

And a final tip:
If you add -whyload to the final link call, the linker will print in the build log which object file from which library it did load because of which symbol in use. It will only print the first symbol considered in use, but that is not necessarily the only symbol in use of that object file.

最后一个提示:如果将-whyload添加到最终的链接调用中,链接器将在构建日志中打印它所加载的库的对象文件,因为它使用的是哪个符号。它只会打印在使用中考虑的第一个符号,但这并不一定是使用该对象文件的惟一符号。

#3


24  

This issue has been fixed in LLVM. The fix ships as part of LLVM 2.9 The first Xcode version to contain the fix is Xcode 4.2 shipping with LLVM 3.0. The usage of -all_load or -force_load is no longer needed when working with XCode 4.2 -ObjC is still needed.

这个问题已经在LLVM中解决了。修复船作为LLVM 2.9的一部分,第一个包含修复的Xcode版本是Xcode 4.2,附带LLVM 3.0。在使用XCode 4.2 -ObjC时,使用-all_load或-force_load不再需要。

#4


16  

Here's what you need to do to resolve this problem completely when compiling your static library:

在编译静态库时,您需要做的是完全解决这个问题:

Either go to Xcode Build Settings and set Perform Single-Object Prelink to YES or GENERATE_MASTER_OBJECT_FILE = YES in your build configuration file.

要么转到Xcode构建设置,要么在构建配置文件中设置为YES或GENERATE_MASTER_OBJECT_FILE = YES的单对象预链接。

By default,the linker generates an .o file for each .m file. So categories gets different .o files. When the linker looks at a static library .o files, it doesn't create an index of all symbols per class (Runtime will, doesn't matter what).

默认情况下,链接器为每个.m文件生成一个.o文件。所以类别得到了不同的。o文件。当链接器查看静态库.o文件时,它不会为每个类创建一个所有符号的索引(运行时将会发生什么)。

This directive will ask the linker to pack all objects together into one big .o file and by this it forces the linker that process the static library to get index all class categories.

这个指令会要求链接器将所有对象打包成一个大的.o文件,并通过这个文件强制连接静态库的链接器获得所有类类别的索引。

Hope that clarifies it.

希望澄清。

#5


9  

One factor that is rarely mentioned whenever the static library linking discussion comes up is the fact that you must also include the categories themselves in the build phases->copy files and compile sources of the static library itself.

在静态库链接讨论时,很少提到的一个因素是,您还必须在构建阶段中包含这些类别—b0拷贝文件和编译静态库本身的源。

Apple also doesn't emphasize this fact in their recently published Using Static Libraries in iOS either.

苹果也没有在最近发布的iOS中使用静态库强调这一事实。

I spent a whole day trying all sorts of variations of -objC and -all_load etc.. but nothing came out of it.. this question brought that issue to my attention. (don't get me wrong.. you still have to do the -objC stuff.. but it's more than just that).

我花了一整天的时间尝试各种各样的-objC和-all_load等等。但是什么也没有发生。这个问题引起了我的注意。(别误会我. .你仍然需要做-objC。但它不仅仅是这样的。

also another action that has always helped me is that I always build the included static library first on its own.. then i build the enclosing application..

另外一个一直帮助我的操作是,我总是首先自己构建包含的静态库。然后构建封闭应用程序。

#6


-1  

You probably need to have the category in you're static library's "public" header: #import "MyStaticLib.h"

您可能需要在静态库的“公共”标题:#import“MyStaticLib.h”中设置类别