可惜没有指出怎么改第三方library名字的具体操作
Item 15: Use Prefix Names to Avoid Namespace Clashes
Unlike other languages, Objective-C has no built-in namespace feature. For this reason, it is easy for names to clash unless measures are taken to avoid the potential. The impact a naming clash has on an application is that it may not link, owing to duplicate symbol errors like this:
duplicate symbol _OBJC_METACLASS_$_EOCTheClass in:
build/something.o
build/something_else.o
duplicate symbol _OBJC_CLASS_$_EOCTheClass in:
build/something.o
build/something_else.o
This error results because the symbol for the class and metaclass (seeItem 14) symbols for a class called EOCTheClass
have been defined twice, from two implementations of a class called EOCTheClass
in twoseparate parts of the application’s code, perhaps from two separate libraries that you are pulling in.
Even worse than not linking would be if one of the libraries that contained one of the duplicates were loaded at runtime. In this case, the dynamic loader would encounter the duplicate symbol error and most likely bring down the entire application.
The only way to avoid this problem is to use a crude form of namespacing: prefixing all your names with a certain prefix. The prefix you choose should be relevant to your company, application, or both.For example, if your company were named Effective Widgets, you might decide to use the prefix EWS
for code common to all your applications and EWB
for code just for the application called Effective Browser. It’s still not impossible to have clashes when you prefix your names, but it is much less likely.
If you are creating applications using Cocoa, it is important to note that Apple has stated that it reserves the right to use all two-letter prefixes, so you should definitely choose a three-letter prefix in this case. For example, an issue could have arisen by not following these guidelines and deciding to use the TW
prefix. When the iOS 5.0 SDK came out, it brought along the Twitter framework, which too uses theTW
prefix, which has a class called TWRequest
for making HTTP requests to the Twitter API. You could very easily also have had a class called TWRequest
if you had an API of your own, for a company called Tiny Widgets, for example.
The prefixing should not stop with class names but should apply to all names that you have within your application. Item 25 explains the importance of prefixing category names and the methods within them if the category is on an existing class. Another important and often overlooked potential for conflict is pure C functions or global variables you use within the implementation files of your classes. It’s often easy to forget that these will appear as top-level symbols within your compiled object files. For example, the AudioToolbox framework in the iOS SDK has a function for playing a sound file. You can give it a callback that gets called when it finishes. You might decide to write a class to wrap this up into an Objective-C class that calls a delegate when the sound file finishes, like so:
// EOCSoundPlayer.h
#import <Foundation/Foundation.h>
@class EOCSoundPlayer;
@protocol EOCSoundPlayerDelegate <NSObject>
- (void)soundPlayerDidFinish:(EOCSoundPlayer*)player;
@end
@interface EOCSoundPlayer : NSObject
@property (nonatomic, weak) id <EOCSoundPlayerDelegate> delegate;
- (id)initWithURL:(NSURL*)url;
- (void)playSound;
@end
// EOCSoundPlayer.m
#import "EOCSoundPlayer.h"
#import <AudioToolbox/AudioToolbox.h>
void completion(SystemSoundID ssID, void *clientData) {
EOCSoundPlayer *player =
(__bridge EOCSoundPlayer*)clientData;
if ([player.delegate
respondsToSelector:@selector(soundPlayerDidFinish:)])
{
[player.delegate soundPlayerDidFinish:player];
}
}
@implementation EOCSoundPlayer {
SystemSoundID _systemSoundID;
}
- (id)initWithURL:(NSURL*)url {
if ((self = [super init])) {
AudioServicesCreateSystemSoundID((__bridge CFURLRef)url,
&_systemSoundID);
}
return self;
}
- (void)dealloc {
AudioServicesDisposeSystemSoundID(_systemSoundID);
}
- (void)playSound {
AudioServicesAddSystemSoundCompletion(
_systemSoundID,
NULL,
NULL,
completion,
(__bridge void*)self);
AudioServicesPlaySystemSound(_systemSoundID);
}
@end
This looks completely innocuous, but looking at the symbol table for the object file created from this class, we find the following:
00000230 t -[EOCSoundPlayer .cxx_destruct]
0000014c t -[EOCSoundPlayer dealloc]
000001e0 t -[EOCSoundPlayer delegate]
0000009c t -[EOCSoundPlayer initWithURL:]
00000198 t -[EOCSoundPlayer playSound]
00000208 t -[EOCSoundPlayer setDelegate:]
00000b88 S _OBJC_CLASS_$_EOCSoundPlayer
00000bb8 S _OBJC_IVAR_$_EOCSoundPlayer._delegate
00000bb4 S _OBJC_IVAR_$_EOCSoundPlayer._systemSoundID
00000b9c S _OBJC_METACLASS_$_EOCSoundPlayer
00000000 T _completion
00000bf8 s l_OBJC_$_INSTANCE_METHODS_EOCSoundPlayer
00000c48 s l_OBJC_$_INSTANCE_VARIABLES_EOCSoundPlayer
00000c78 s l_OBJC_$_PROP_LIST_EOCSoundPlayer
00000c88 s l_OBJC_CLASS_RO_$_EOCSoundPlayer
00000bd0 s l_OBJC_METACLASS_RO_$_EOCSoundPlayer
Note the symbol lurking in the middle, called _completion
. This is the completion function created to handle when the sound has finished playing. Even though it’s defined in your implementation file and not declared in the header file, it still appears as a top-level symbol like this. Thus, if another function called completion
is created somewhere, you’ll end up with a duplicate-symbol error when linking, such as the following:
duplicate symbol _completion in:
build/EOCSoundPlayer.o
build/EOCAnotherClass.o
Worse still would be if you were shipping your code as a library for others to use in their own applications. If you exposed a symbol called_completion
like this, anyone using your library would not be able to create a function called completion
, which is a nasty thing for you to do.
So you should always prefix C functions like this as well. In the preceding example, you could call the completion handlerEOCSoundPlayerCompletion,
for example. This also has the side effect that if the symbol ever appears in a backtrace, it’s easy to determine what code the problem has come from.
You should be particularly careful of the duplicate-symbol problems when you use third-party libraries and ship your code as a library that others plug into their own applications. If the third-party libraries that you use are also going to be used by the application, it’s easy for duplicate-symbol errors to arise. In that case, it’s common to edit all the code of the libraries you use to prefix them with your own prefix.For example, if your library is called EOCLibrary
and you pull in a library called XYZLibrary,
you would go through and prefix all names in XYZLibrary
with EOC. The application is then free to useXYZLibrary
itself, without the chance of a naming collision, as shown in Figure 3.1.
Going through and changing all the names might seem like a rather tedious thing to do, but it’s prudent if you want to avoid naming collisions. You may ask why it’s necessary to do this at all and why the application can’t simply not include XYZLibrary
itself and use your implementation of it. It’s possible to do that as well, but consider the scenario in which the application pulls in a third library, ABCLibrary
, that has also decided to use XYZLibrary
. In that case, if you and the author of ABCLibrary
hadn’t prefixed, the application would still get duplicate-symbol errors. Or if you use version X of XYZLibrary
but the application requires features from version Y, it would want its own copy anyway. If you spend time using popular third-party libraries with iOS development, you will frequently see this kind of prefixing.
Things to Remember
Choose a class prefix that befits your company, application, or both. Then stick with that prefix throughout.
If you use a third-party library as a dependency of your own library, consider prefixing its names with your own prefix.