Global Variables for Class Methods - objective-c

Background
In Cocoa, Apple frequently makes use of the following paradigm:
[NSApplication sharedApplication]
[NSNotificationCenter defaultNotificationCenter]
[NSGraphicsContext currentContext]
[NSCalendar currentCalendar]
and so on.
They also will occasionally make use of a paradigm that I feel is far more legible when working with vast amounts of code.
NSApp //which maps to [NSApplication sharedApplication]
Goal
I'd love to be able to utilize this sort of global variable, both in my own classes, and in extensions to other classes.
MYClassInstance
NSDefaultNotificationCenter
NSCal /* or */ NSCurrentCalendar
and so on.
The "duh" Approach
#define. Simply #define NSCal [NSCalendar currentCalendar], but as we all know by now, macros are evil (or so they say), and it just doesn't seem like the right Cocoa way to go about this.
Apple's Approach
The only source I could find regarding NSApp was APPKIT_EXTERN id NSApp;, which is not exactly reusable code. Unless I'm mistaken, all this code does is define NSApp to be an id the world around. Unfortunately unhelpful.
Close, but not Quite
In my searches, I've managed to find several leads regarding "global constants", however things like this:
extern NSString * const StringConstant;
are unfortunately limited to compile-time constants, and cannot map to the necessary class method.
Bottom Line
I'd love to be able to roll my own NSApp-style global variables, which map to class methods like [NSNotificationCenter defaultNotificationCenter]. Is this possible? If so, how should I go about it?
Further Attempts
I'm trying to implement specifically the framework singletons in the following way:
MySingletons.h
//...
extern id NSNotifCenter;
//...
MySingletons.m
//...
+(void)initialize
{
NSNotifCenter = [NSNotificationCenter defaultCenter];
}
//...
MyAppDelegate.m
//...
#import "MySingletons.h"
//...
//in applicationDidFinishLaunching:
[MySingletons initialize];
NSLog(#"%#", NSNotifCenter);
//...
However, this results in a compile-time error where the _NSNotifCenter symbol cannot be found.
Goal!
I'm currently working on an Objective-C class to encapsulate some of the framework singletons I've referred to in this question. I'll add the GitHub information here when I get it up.

That's funny, I just made this suggestion on another question.
You just expose the variable that holds the singleton instance as a global itself. NSApp isn't actually mapping to a sharedApplication call. It's a regular old pointer; it was set up during the application launch process to point to the same instance that you would get back from that call.
Just like NSApp, you declare the variable for any file which imports the header:
extern MySingleton * MySingletonInstance;
in the header (you can use APPKIT_EXTERN if you like; the docs indicate that it just resolves to extern in ObjC anyways).
In the implementation file you define the variable. Usually the variable holding the shared instance is declared static to confine its linkage to that file. If you remove the static, the statement defines storage that is "redeclared" in the header.
Then, use it as you did before. The only caveat is that you still have to get your singleton setup method [MySingleton sharedInstance] called before the first time you use the global in order to make sure it's initialized. -applicationDidFinishLaunching: may be a good candidate for a place to do this.
As for creating pointers to framework singletons, you can just stash the result of [CocoaSingleton sharedInstance] in whatever variable you like: an ivar in a class that wants to use it, a local variable, or in a global variable that you initialize very early in your program via a function you write.
The thing is, that's not guaranteed not to cause problems. Except in the case of NSApp (or unless it's documented somewhere) there's really no guarantee that the object you get back from any given call to sharedInstance is going to remain alive, valid, or useful past the end of your call stack.
This may just be paranoia, but I'd suggest not doing this unless you can find a guarantee somewhere that the supposed singletons you're interested in always return the same instance. Otherwise, you might suddenly end up with a dangling global pointer.
Addressing your code, the declaration in your header doesn't create a variable. You still need a definition somewhere:
// MySingletons.h
// Dear compiler, There exists a variable, NSNotifCenter, whose
// storage is elsewhere. I want to use that variable in this file.
extern id NSNotifCenter;
// MySingletons.m
// Dear compiler, please create this variable, reserving memory
// as necessary.
id NSNotifCenter;
#implementation MySingletons
// Now use the variable.
// etc.
If you're creating a singleton, you might want to glance at Apple's singleton documentation.

The existing discussion here was so intriguing that I did a little research and discovered something I'd never realized before: I can #import a header file from my own project into the project's .pch file (the precompiled header). This header file becomes automatically visible to all the other class files in my project with no effort on my part.
So here's an example of what I'm now doing. In the .pch file, beneath the existing code:
#import "MyIncludes.h"
In MyIncludes.h are two kinds of thing, categories and externs (the latter in accordance with Josh's suggestion):
extern NSString* EnglishHiddenKey;
extern NSString* IndexOfCurrentTermKey;
#interface UIColor (mycats)
+ (UIColor*) myGolden;
+ (UIColor*) myPaler;
#end
In MyIncludes.m we provide definitions to satisfy all the declarations from the header file. The externs don't have to be defined from within any class:
#import "MyIncludes.h"
NSString* EnglishHiddenKey = #"englishHidden";
NSString* IndexOfCurrentTermKey = #"indexOfCurrentTerm";
#implementation UIColor (mycats)
+ (UIColor*) myGolden {
return [self colorWithRed:1.000 green:0.894 blue:0.541 alpha:.900];
}
+ (UIColor*) myPaler {
return [self colorWithRed:1.000 green:0.996 blue:0.901 alpha:1.000];
}
#end
Except for the part about using the pch file to get magical global visibility, this is not really any different from Josh's suggestion. I'm posting it as a separate answer (rather than a mere comment) because it's long and needs formatting, and the explicit code might help someone.
(Note that there is no memory management, because I'm using ARC. The externs leak, of course, but they are supposed to leak: they need to live as long as the app runs.)

Related

Objective-C : Accessing fields in implementation

Is it possible to fields defined only in implementation but not in interface definition ?
#interface MyInterface .... #end --> dict not defined here!!!
#implementation MyInterface
...
NSDictionary *dict;
...
#end
In this case if somewhere I somehow accessed to this class, can I access to the dict or should I create a getter just like in Java ?
Edit after #Rob's answer
Thanks for the answer Rob, I wish I have the implementation of these interface and classes. Instead I am trying to bind two different libraries ( I know it is reallllly bad as architectural point of view but this is where I end up).
Basically, I am in react-native world. And we are using react-native-video as our player layer. But since AVPlayer does not support some subtitle types our head company sent us a library that needs a player instance and a view instance to draw subtitle on the view. I believe they will bind to events of the player and draw sub titles based on player states.
So react-native-video is in this github repo with the interface and implementation.
I find the UIView that includes the properties and casted it to the object itself RTCVideo in this case). But now I am stuck. I can go and change some stuff as per your suggestion in the "Development Pods" to be able to access but this is my last bullet :) I prefer to convince these two libraries in a friendly way :)
Yes, but the above syntax isn't what you want. The modern way to do this is with a class extension.
Your header file is the same:
#interface MyInterface
#end
But in your .m file, you create an extension by appending ():
#interface MyInterface ()
#property (nonatomic, readwrite) NSDictionary *dict;
#end
Now, inside your .m file, you can access self.dict normally, but outside of your .m file it won't appear available.
For full details, see Programming with Objective-C: Class Extensions Extend the Internal Implementation.
The syntax you've written actually creates a static (global) variable called dict that isn't tied to any instance.
It is possible to create raw instance variables using a {...} syntax, either on the extension or on the implementation, but this isn't used that often today, except for managing raw buffers that you don't want accessors for. The syntax is either:
#interface MyInterface () {
NSDictionary *_dict;
}
...
#end
or on the implementation:
#implementation MyInterface {
NSDictionary *_dict;
}
...
#end
But I recommend simple extensions with properties any time you can. And if you are considering creating an accessor for it, you definitely want to use #property and let the system do it for you.
If I understand your edits correctly, you're trying to read the internal ivars of an object that doesn't expose them with an accessor, correct? I believe specifically you want to access _player.
There's several common ways to do that. The key feature you want is Key-Value Coding.
The simplest approach for this problem is -valueForKey:
AVPlayer *player = [view valueForKey:#"player"];
The first thing -valueForKey: looks for is _<key>, and if it's just an object pointer (as in this case), it just returns it.
(This can be broken if a class return false for +accessInstanceVariablesDirectly, but the default is true, and it's unusual to override this.)
Another very common approach is to just declare any methods you know exist as a category. (This won't work for _player, since it's not a method, but in case you need similar things.) Imagine you wanted to call the "private" method -removePlayerTimeObserver. In your .m file, just say you know about it using a category:
#interface RCTVideo (PrivateMethods)
- (void)removePlayerTimeObserver;
#end
And since you know about it, you can call it:
[video removePlayerTimeObserver];
If you're wrong, and that method doesn't really exist, then the program will crash. In Objective-C, almost all rules are advisory. You can break them if you want to. ObjC programmers tend to be big rule-followers because otherwise the program crashes and ObjC has very clear rules that are pretty easy to follow. It's not because the system forces us to.

Global variables not initializing

I have found several explanations how to fix this but none of them (that I found) are simple enough for me to understand. New to Objective-c.
I am looking to access an array in multiple classes in objective c. I am trying to create a global variable (I know inefficient) in a "Globals.h" class, Initialize them in another class, and access that information in yet another class.
Heres what I have.
In Globals.h
#import <UIKit/UIKit.h>
extern NSArray *globalRows;
#interface Globals : NSObject
#end
ViewController.m
#import "Globals.h"
-(void)initGlobal{
globalRows = [[NSArray alloc] initWithArray:rows];
}
The compiler does not want to Initialize the variable and I do not understand why.
EDIT:
The Globals class is only to hold the global variables there is no executable code in this class.
The reason I initialize in the ViewController is that is where the information needed is parsed and stored.
There is no error and the code will simply not compile.
EDIT2:
It seems I have found a very good explanation here!
NSObjects have a + (void)initialize method you can setup that get called during startup. You could also just call it from main as well.
Still, a better design pattern is to make a singleton to hold some global thing you want to access/use. Similarly putting the things inside your app delegate make them effectively global.

How do you suppress linker warning when overriding a class instance method in a category

I need to override a method using a category. I'm also aware of the dangers with doing this (this is a private class inside another class and noone will ever write another overriding category method, so no undefined behaviour guaranteed). I've seen a lot of similar questions but they all address suppressing the compiler warning using something like this:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
// do your override
#pragma clang diagnostic pop
However this still leaves the linker warning. Is it possible to get rid of it for my particular override which I deem safe, in Xcode 4.6?
Here's a sample GitHub project illustrating the problem.
OK, as I explained in my comment what you are trying to do is dangerous and should not be done. I also suggested to read the runtime documentation to understand why and learn about other methods to achieve your goal. You should read that.
In any case an alternative to what you are doing, that produces the exact same results without raising so many red flags, is to use the runtime environment to "skip" one class in the initialization hierarchy, effectively "overriding" the superclass method.
Here's one option of how it's done, in your example project, change the FunkyBranch class implementation to this:
#import "FunkyBranch.h"
#import <objc/runtime.h>
typedef id(*InitIMP)(id,SEL);
#implementation FunkyBranch
-(id) init
{
InitIMP superSuperInit = (InitIMP)class_getMethodImplementation([[self superclass] superclass], #selector(init));
self = superSuperInit(self, #selector(init));
if (self)
{
NSLog(#"FunkyBranch initialized");
}
return self;
}
#end
It will have the same results as your current implementation without the dangers of what you are doing.
Please keep in mind that casting the function pointer to the correct type is of utmost importance, also I still believe that you should rethink your approach instead of forcing the runtime to do something it's not designed to do. In any case, this answers your question.

Why don’t iOS classes adopt copyWithZone protocol to encourage active mem mgt?

Recently turning to iOS after having worked with Cocoa, I was startled to get a SIGABRT with the following error: “-[UIDeviceRGBColor copyWithZone:]: unrecognized selector sent to instance…” I had called “copy” on a UIColor.
I looked at the class references and, zounds, UIColor does not adopt any protocols, in contrast to NSColor.
Now, this is not a big deal. I was just attempting to be more efficient by taking active ownership of a color instance so as to discard it immediately after use. But I thought the purpose behind Apple’s omitting a garbage collector in iOS was to encourage developers to do exactly what I was doing, to keep a lean memory profile on the memory-starved, battery-challenged portable devices.
Any ideas on Apple’s rationale, or is there some error in my assumptions?
I don't understand why you think implementing the NSCopying protocol would "encourage active memory management".
Since UIColor is immutable (it implements no methods that change its internal state), there is no point making a copy. Just retain it if you want to keep it around, and release it when you're done. There is no need for anything else.
If you really wanted, you could add copying in a category:
#implementation UIColor (Copying) <NSCopying>
- (id)copyWithZone:(NSZone *)zone
{
return [self retain];
}
#end
But obviously that doesn't actually give you any new functionality. Apparently Apple didn't think it was worth the time when they implemented that class.
My app needs to work on both iOS5 (UIColor>>#copyWithZone doesn't exist) and iOS6+ (UIColor>>#copyWithZone exists) so I came up with the following:
#implementation UIColor(ios5CopyWithZone)
+ (void)initialize
{
// iOS5 dosn't include UIColor>>#copyWithZone so add it with class_addMethod.
// For iOS6+ class_addMethod fails as UIColor>>#copyWithZone already exists.
Class klass = [UIColor class];
Method methodToInstall = class_getInstanceMethod(klass, #selector(ios5CopyWithZone:));
class_addMethod(klass, #selector(copyWithZone:), method_getImplementation(methodToInstall), method_getTypeEncoding(methodToInstall));
}
// UIImage is immutable so can just return self.
// #retain to ensure we follow mem-management conventions
-(id)ios5CopyWithZone:(NSZone *)__unused zone
{
return [self retain];
}
#end
The code attempts to adds UIColor>>#copyWithZone using the runtime's class_addMethod. I don't know if this is any better than implementing UIColor>>#copyWithZone directly in a category, however reading Apple's Avoid Category Method Name Clashes implies that it is bad practice to reimplement an existing framework method (that is UIColor>>#copyWithZone in iOS6). However I realise that +initialize could potentially trample on a framework's +initialize.

Objective C shenanigans

In my quest to be the grandmaster of Objective C, I keep running into it's subtleties, which I want to share with ya'll and gain an understanding why
1) I have two init methods, the one that is inherited by NSObject for my Objective C class and one is a custom method that I create off my own, let's say
initCustomMethod:(int)par1 argument2:(int)par2;
My Aim is to call initCustomMethod through the provided init method, essentially
-(id)init{
return [self initCustomMethod:1 argument2:3];
}
Naturally, maintaining the order, I have init appearing before initCustomMethod in the .m file. Xcode warns me telling me that the initCustomMethod is not found, I go ahead and shuffle the order and have init appearing after initCustomMethod is declared and there is no such warning message anymore and everything is fine.
I concur that the order is important since it's essentially derived from C, however I am not sure of this. Because, i shuffled the order of some properties and their custom methods, with the properties #synthesize being declared after the custom setter method for a given property, but there was no such error replica.
Can anyone point out the malice here?
Thanks guys!!!
Very cool guys, thanks for helping me out with this. Also, since I have a custom init method, I am initializing the super in this method and using the original init method to call the custom init method.
Anything wrong with this?
Before you reference it anywhere, you should declare initCustomMethod:argument2 in your interface, which would usually be in your header file.
For example, you would usually have a .h file that looks like:
#interface MyClass
{
//instance variables
int anInstanceVariable;
}
// properties
#property (nonatomic, assign) int aProperty;
// methods
-(id)initCustomMethod:(int)par1 argument2:(int)par2;
#end
And if you did this, the order in which you define init and initCustomMethod:argument2: won't matter. This is because the declaration tells the compiler that you are going to define the method, and what it will look like, so it isn't confused when you use it later.
It's a bad idea in Objective-C to use a function or a method before it is either declared or defined. Putting initCustomMethod:argument2: before init means that the former is already defined in the latter. But if you'd just declare it in the header, it wouldn't matter which order they went in.
Add your custom method name in your header file - the compiler just goes through things in order. If you don't have a forward declaration, the compiler won't know what to do with that call. You're going to need to put it in the header if you want other parts of your program to be able to call it anyway.