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

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.

Related

Objective-C "Override" Specifier

In Java, C++11 and some other languages you can specify that a method is intended to override another method from a base class or interface, if you then at a later point remove the method from the base class you get a compiler error. I use protocols with optional methods a lot and find that if I remove a method from there I have a tendency to forget to remove the code that implemented the method. This does not generate an error or a warning, thus creating a "dead" method.
Consider:
#protocol Prot <NSObject>
#optional
- (void)bar;
- (void)tempBar;
#end
#interface MyType : NSObject <Prot>
#end
#implementation MyType
- (void)bar { /**/ }
- (void)tempBar { /**/ }
#end
If I at one point remove tempBar from the protocol, I would like to get at least a warning from tempBar being implemented in MyType.
Is there any way in Objective-C to specify that a method is expected to be an implementation of a protocol method?
Objective-C is a dynamic language and this is rather impossible to enforce at compile time. Note that in Obj-C you can actually call methods there are not even there and the app won't crash (well, the default implementation will raise an exception but you can change that behavior).
The method can be also added in an extension, or added at runtime. Or it is just not present in the header.
Note there is also the opposite problem. When subclassing, you can override a method which you don't even know is there, because it is not present in the headers.
This is one of the reasons why Apple is moving to a more predictable language, that is, Swift.

Private Method Implementation

This question is mostly curiosity than anything else. But I currently place all my private methods first in my #implementation so that I can avoid creating an a separate category in my .m file for those methods. As long as the private method was implemented before any other method called it, all was good. I can distinctly remember Xcode warning me if I tried to call a non-declared method before its implementation....at least, I think I can. Now I'm starting to doubt my sanity a little cause Xcode now seems perfectly happy to allow me to call any non-declared method as long as its implementation is located anywhere within the #implementation, even if the call comes before the implementation.
Is this a recent change or have I been structuring my method order off of some archaic 'C' limitation?
The way Xcode is behaving now, it seems there's no need to create any kind of category for private methods. Personally, I find this quite nice.
EDIT: I'm using Xcode 4.3.1
Apparently you are right.
xcode 4.2.1 issues a warning and 4.3.1 does not.
#implementation MyClass
- (void) callMyPrivateMethod {
[self myPrivateMethod]; //<--- xcode 4.2.1 issues a warning here.
return;
}
- (void) myPrivateMethod {
return;
}
#end
(I know there is no need for the 'return's but I am a bit old fasioned with this respect.)
However, both versions will build it properly and it will runn unless you made a typo in the method name.

Global Variables for Class Methods

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.)

Objective-C dynamically created methods and compiler warnings

If I generate methods dynamically on runtime and then call them - how can I convince compiler that the class will respond to undeclared (generated) methods and make it not throw warnings?
Update in regard to answers
When I generate the methods - their name is not known at compile time. To give an example - if I have a view controller MyFooController and it's initiated with method initWithFoo:(Foo*)foo, I'd be able to generate method like pushMyFooControllerWithFoo:(Foo *)foo for UINavigationController. Hence you notice that declaring such methods would be counter-productive.
This doesn't directly answer your question, but if I was generating method names (presumably from strings), I would call them using the string names, hence bypassing the compiler warnings.
[fooController performSelector:NSSelectorFromString(#"pushMyFooControllerWithFoo:") withObject:foo];
That way you are responsible for the validity of the generated method names.
Since you are adding methods on runtime, so you should also invoke them with runtime function, objc_msgSend or performSelector:withObject:for example, so the compiler will not going to warn you anything.
Well, if you call them, you know their signature, and if you know their signature, you can declare them, can't you?
Declare this method in a category for NSObject and make an empty implementation:
#interface NSObject (DynamicMethodsCategory)
- (void)doSomething;
#end
#implementation NSObject (DynamicMethodsCategory)
- (void)doSomething
{
}
#end
In your object you can call it without any warnings:
#implementation MyObject
- (void)someMethod
{
[self doSomething];
}
#end
Then generate implementation of [MyObject doSomething] dynamically, it will be called instead of NSObject's one.
Update:
Alternatively, the method can be declared in a category for the object. This suppresses the compiler's Incomplete implementation warning. However, I think this is not a good workaround, because the application will crash if the method is not created dynamically in runtime before it is called.

Accessing private variable in Category results in linker error

EDIT: I'm not going to do this, I now realize how dangerous this can be. But, the question stays for purely academic purposes.
I'm trying to implement a category on NSCollectionView that will let me access the private variable _displayedItems. I need to be able to access it in my subclass. So, I've created the following category:
#interface NSCollectionView (displayedItems)
- (NSMutableArray *)displayedItems;
#end
#implementation NSCollectionView (displayedItems)
- (NSMutableArray *)displayedItems
{
return _displayedItems;
}
#end
...which seems like it should work perfectly. However, when I try to compile this, the linker gives me the following error:
Undefined symbols:
"_OBJC_IVAR_$_NSCollectionView._displayedItems", referenced from:
-[NSCollectionView(displayedItems) displayedItems] in NSCollectionView+displayedItems.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
I know for a fact that _displayedItems exists in NSCollectionView, I've looked at the interface and also printed it's contents using gdb. Does anyone know of a way to fix this?
Thanks in advance!
Billy
_displayedItems is a private ivar, so you shouldn't access it, even from a category.
That said, you should try compiling the same code with
gcc -arch i386
and
gcc -arch x86_64
and see the difference. In the 32 bit mode you don't see the error. This shows how fragile the situation is. You really shouldn't.
That said, there's a way to get that ivar by abusing KVC:
#implementation NSCollectionView (displayedItems)
- (NSMutableArray *)myDisplayedItems
{
return [self valueForKey:#"displayedItems"];
}
#end
Note that you shouldn't name your method just as displayedItems. That would make an infinite loop, because the KVC machinery would find your method earlier than the ivar. See here.
Or you can access any hidden ivar using Objective-C runtime functions. That's also fun.
However, let me say again. There's a big difference in knowing you can do one thing and doing that thing for real. Just think of any hideous crime. and doing that by yourself.
DON'T DO THAT!!!!!
You shouldn't really, but access it like a pointer to a member of a struct:
-(NSMutableArray *)displayedItems {
return self->_displayedItems;
}
This is a fragile thing to do, as I'm sure you're aware however ;)
UPDATE: Since you've mentioned the above doesn't work, try dropping down to the runtime:
-(NSMutableArray *)displayedItems {
NSMutableArray *displayedItems;
object_getInstanceVariable(self, "_displayedItems", (void *)&displayedItems);
return displayedItems;
}
(Tested, works)