I was watching the F8-2016 Building iOS Tooling at Facebook Scale video, when I've noticed an interesting code part at 7:01.
Facebook defined a static constant in Objective-C this way:
static __unsafe_unretained NSString * const kAuthorKey = #"AUTHOR";
Up to now, I declared my static constants the same way, but without the __unsafe_unretained. Without this modifier, the constant will be strong, but since it exists always during the application run, it doesn't matter if it is strong or __unsafe_unretained.
Am I missing something? Do Facebook has any reason to use __unsafe_unretained?
Related
My company develops an advertising SDK that mediates other ad networks. At runtime, it loads constants from those SDKs using CFBundleGetDataPointerForName, as outlined in this StackOverflow post.
However, I'm not able to get that approach to work to load the GADAdSize constants that AdMob's banner SDK uses. Here's what my code looks like:
HZGADAdSize *hzlookupAdMobAdSizeConstant(NSString *const constantName) {
return CFBundleGetDataPointerForName(CFBundleGetMainBundle(), (__bridge CFStringRef)constantName);
}
HZGADAdSize *hzAdMobAdSizeFlexibleWidthPortrait(void) {
return hzlookupAdMobAdSizeConstant(#"kGADAdSizeBanner");
}
The above code works fine for loading similar constants, like Facebook's FBAdSize struct, but the returned pointer is NULL when trying to load any GADAdSize constant.
Here's how the constant is defined in the AdMob SDK:
/// Do not create a GADAdSize manually. Use one of the kGADAdSize constants. Treat GADAdSize as an
/// opaque type. Do not access any fields directly. To obtain a concrete CGSize, use the function
/// CGSizeFromGADAdSize().
typedef struct GADAdSize {
CGSize size;
NSUInteger flags;
} GADAdSize;
#pragma mark Standard Sizes
/// iPhone and iPod Touch ad size. Typically 320x50.
extern GADAdSize const kGADAdSizeBanner;
Things I've tried so far
Trying with all bundles in the app:
CFArrayRef array = CFBundleGetAllBundles();
for (int i = 0; i < CFArrayGetCount(array); i++) {
CFBundleRef bundle = (CFBundleRef)CFArrayGetValueAtIndex(array, i);
void *ptr = CFBundleGetDataPointerForName(bundle, (__bridge CFStringRef)#"kGADAdSizeBanner");
if (ptr == NULL) {
NSLog(#"pointer was NULL");
} else {
NSLog(#"pointer was present!!");
}
}
None of the bundles in the app have the constant, so I don't think its an issue of the pointers being in a different bundle.
Marking the constants as extern in my code
extern HZGADAdSize const kGADAdSizeSmartBannerPortrait;
The extern approach lets me reference the constants, but if AdMob isn't present when the app is compiled, I get compiler errors about the missing constants, so I don't think this option is feasible since many of our developers won't use AdMob.
Loading function pointers from the same class. Edit: I was mistaken; I can't load function pointers either. I also can't load string constants.
void *ptr = CFBundleGetFunctionPointerForName(CFBundleGetMainBundle(), (__bridge CFStringRef)#"CGSizeFromGADAdSize"); // returns NULL
To avoid XY problems: Our SDK loads constants at runtime so that if e.g. the AdMob SDK is present, we can pass the constants it defines to it. If the SDK isn't present (e.g. when the developer isn't using AdMob but is using other ad networks), we still need our SDK to compile without AdMob being there. If there's a way we can accomplish this without loading values at runtime, that's just as good a solution for me.
Version Information:
AdMob SDK 7.0.0
iPhone 6 Simulator
iOS 8.2
Xcode 6.2
In my iOS/Objective C projects, I often have a constants.h file with my API keys and such. Until today, I've been declaring my constants as static const like so:
static NSString * const kAPIKey = #"wembvkejrvb43789gvbiu2bvfake";
This works fine, but with the unfortunate downside that I can only create constants for primitives and NSString literals, essentially. Other objects, such as UIColor objects, cannot be stored in this constant as they cannot be initialized with a static literal syntax (my understanding, citation required).
After reading some C++ docs, I understand a few things:
The static is unnecessary, as const are implicitly static.
Calling NSString * const x is actually declaring a constant and immutable value in x. I cannot change the value, but may be able to change what x points to.
This const has internal linkage, meaning the the value is defined right away (presumable at compile time).
Are these conclusions correct?
How does an extern const differ? I assume they are externally linked (thus the extern keyword). Are they defined at run time? Can I create some sort of dynamic extern const that can be set with a value returned by a class method?
For example, I would like to create a globally-scoped constant that contains a UIColor value. I would like to construct this color value using the [UIColor colorWithRed:green:blue:alpha:] class method. This clearly doesn't work with the internally linked constants I've been using (I'm assuming because it happens at compile-time) - but is it possible using an external constant, possibly set up in the +initialize method?
Any elaboration on the details of this behavior would be immensely helpful.
The static is unnecessary, as const are implicitly static.
No, that's not true.
static when used at file scope (i.e. outside any method or function) means that the variable is visible only within that file.
extern means that the variable is defined in some other file.
const means that the variable cannot be modified.
Consider strings. Often, you'll have an implementation file (name ends in .m) that defines some constant string pointer:
NSString *const SomeString = #"some string";
You might want to use that same constant from other files. If so, you could add a declaration to the header (name ends in .h) file that explains to the compiler that the variable is defined elsewhere:
extern NSString *const SomeString;
and that would let you use SomeString in any file that imports the header file. On the other hand, you might decide that you definitely do not want the constant used outside the implementation file. In that case, you could declare it static (in the implementation file again):
static NSString *const SomeString = #"some string";
and that would prevent its use from outside the file.
Calling NSString * const x is actually declaring a constant and immutable value in x. I cannot change the value, but may be able to change what x points to.
Right, it declares the pointer x to be constant -- you can't change it. You also can't change the value that it points to if it's actually a NSString because an instance NSString isn't mutable.
This const has internal linkage, meaning the the value is defined right away (presumable at compile time).
I'll take the 5th on that -- I'm not sure exactly how the compiler deals with constant strings. I think it's safe to use that as a mental model, though; the string will in any case be defined before your code ever gets to use it.
On your specific programming question, how to create a compile-time defined color object: you can't, because, apart from the handful for which the language supplies literal syntax, objects are created at runtime.
But you can still do it elegantly at runtime, without any addition to global scope, the same way the sdk does...
#interface UIColor (RainbowAddition)
+ (UIColor *)chartruseColor;
#end
#implementation UIColor (RainbowAddition)
+ (UIColor *)chartruseColor {
// bonus: this is really chartruse... according to the internet
return [self colorWithRed:0.5 green:1.0 blue:0.0 alpha:1.0];
}
#end
http://cloford.com/resources/colours/500col.htm
Objective-C is a pure extension of C, C++ is not.
At global scope:
in C (and Objective-C) writing const is equivalent to extern const (external linkage);
in C++ writing const is equivalent to static const (internal linkage).
Both in C (and Objective-C) and C++, to create a global-scoped const you can define it, just one time, in one source file like extern const TYPE identifier = VALUE; and declare it (typically in an header file) like: extern const TYPE identifier; (read it: I defined this const elsewhere at global linkage level).
I've find it frustrating to define constants in the .h as externals and then assign the constants in the .m file. Seems so redundant. Is there any reason not to just define the constants in the header file?
Typical implementation would be:
// Constants.h
#interface Constants : NSObject
extern NSString *const kPCFavorites;
#end
The implementation would then be:
// Constants.m
#implementation Constants
NSString *const kPCFavorites = #"PCFavorites";
#end
However, I can just do this:
// Constants.h
static NSString *const kPCFavorites = #"PCFavorites";
#interface Constants : NSObject
#end
Obviously, this last definition doesn't even need an interface or implementation so both could be left out and become:
// Constants.h
static NSString *const kPCFavorites = #"PCFavorites";
with no .m file at all.
This seems much cleaner to me. Why wouldn't we implement constants this way? I've defined them both ways and I get no compile or runtime errors in XCode 5.
Because
static NSString * const kPCFavorites = #"PCFavorites";
declares a variable, not a constant. C doesn’t actually have a way to declare a symbolic constant (besides enum, which only works for integers).
As a result, if you use this method, every file that #includes your header will have its own variable called kPCFavorites. Historically, that would have meant that your program would increase in size because of all of the copies of kPCFavourites and the string #"PCFavourites", although more modern linkers may manage to get rid of some or all of the duplication (certainly I’d expect the linker to leave you with only one copy of the string itself; whether or not it can currently get rid of the extra pointer variables I’m not sure — but it’s easy to test).
static variables have file scope. If you compile a file which includes a header file with a static variable, that variable will exist in the compiled file. If you compile another file with the same header file, you have a second static variable and so on. If you include the header from 1000 source files, you get 1000 static variables, all with the same name.
According to this answer, I'm trying to declare and define global variable and getting this error:
Undefined symbols for architecture armv7:
"_loggedIn", referenced from:
-[XYZAppDelegate application:didFinishLaunchingWithOptions:] in XYZAppDelegate.o
-[XYZFolderViewController viewDidAppear:] in XYZFolderViewController.o
-[XYZFolderViewController loginViewController:didEnterUsername:password:] in XYZFolderViewController.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I've found many answers here, but all of them are referring to different problem(s) (as much as I can say).
XYZAppDelegate.h
...
extern BOOL loggedIn;
...
XYZAppDelegate.m:
...
#implementation XYZAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
loggedIn = 0;
...
}
...
XYZFolderViewController.h
...
extern BOOL loggedIn;
...
XYZFolderViewController.m
...
#implementation
...
- (void)loginViewController:(XYZLoginViewController *)loginViewController
didEnterUsername:(NSString *)username
password:(NSString *)password
{
...
if ([username isEqualToString:theUsername] && [password isEqualToString:thePassword])
{
loggedIn = 1; /* 0 == not logged in. 1 == logged in. */
...
}
...
}
Point of this global variable is holding information about user being logged in (or not) into app.
I am aware that using extern in Objective-C is not clean approach. However, besides extern usage, I'd appreciate hints for solutions of other kind.
Expecting this is rookie question.
extern means: "this thing is defined somewhere else"
So, you aren't declaring a global variable. You're just saying that someone else is and that you want to use it. So when the compiler tries to find it and comes up blank it tells you it couldn't find the definition.
Not that you should be trying to use a global for this, you shouldn't, but it would be done using static.
You should make the loggedIn flag an instance variable and provide proper, read only, access to the value. This means publicly read only, but privately writeable. So that the status can only be changed inside the class (as a result of loginViewController:...).
So, in the .h file you would have:
#property (assign, nonatomic, readonly) BOOL loggedIn;
And then in the .m file:
#property (assign, nonatomic, readwrite) BOOL loggedIn;
Making state globally available in your app is generally not a good idea. You should always try to keep mutable state to the minimum. Take on this read or check out the Functional Programming Priciples Course these are excellent to get you started on becoming a better programmer.
The extern keyword is best for making constant objects available everywhere. E.g. NSStrings. Like this:
Constants.h
extern NSString* kAKeyPath;
extern double kPI;
Constants.m
NSString * const kAStringContant = #"contact.name";
const double kPI = 3.14159265359;
Using values in this way is advantageous as the compiler can check it for you (it's not just a string or int value you need to remember) and it will be stored in one instance in the program data store (compared to storing it as many times as they appear if extern is not used).
one issue could be if you have multiple targets, you have imported the .h file which includes a promise that a variable exists, but you aren't including the .c or.m so you are missing the compilation unit that has the symbol. Of course you could manage to get into this state without multiple targets, but since Xcode will add the .m automatically to the default target, you typically won't get into that state.
I have a library of my own. And there are lots of constants defined in headers (for example animation duration). But, all my headers are visible and changeable. How can I prevent others to change my default values?
There are some const value in headers of Apple libraries like this:
CA_EXTERN NSString * const kCATransitionMoveIn
__OSX_AVAILABLE_STARTING (__MAC_10_5, __IPHONE_2_0);
Objective-C is still, well, C. Maybe older systems had issues, which is why you see the macros there. Anyway, you should still be safe with any built-in type.
If you declare something as "extern" the compiler will treat it something like, "OK, I see that someone has declared and external thingy. I don't have to know what it is, because some external unit will define it. The linker will handle the rest.
That paragraph will get me in trouble with the C-police, but it's close enough for a practical explanation. Thus, you can do this in your header file...
extern int const TheAnswerToLifeTheUniverseAndEverything;
And then, in one of your implementation files (outside of the #implementation/#end section)...
int const TheAnswerToLifeTheUniverseAndEverything = 42;
Note, in "modern" Xcode versions, you can do the same thing with objects. Note the "const" which means we have a constant-pointer-to-NSString.
// In header
extern NSString * const TheAnswerToLifeTheUniverseAndEverythingString;
// In source
NSString * const TheAnswerToLifeTheUniverseAndEverythingString = #"42";