Private Method Implementation - objective-c

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.

Related

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.

Can you inherit from a class in objc not available at compile time?

I have code inheriting from UIActivity introduced in iOS 6 which compiles fines with Xcode 4.5 and works fine on iOS 6 and previous versions (I detect availability of the class at runtime). However, is it possible to make this code compile with Xcode 4.4, which does not include the UIActivity class in its SDK?
If I forward declare UIActivity the dealloc method doesn't compile because it calls super and the compiler warns me the class is already at the root of the inheritance tree. Maybe there is a way to make this class inherit from a proxy class which I define locally, and then at runtime somehow swizzle it and instantiate it as if it had been properly defined at compile time? The purpose of this is to compile code with Xcode 4.4 and have the binary run on iOS 6 using that phantom class.
I know I can use defines to prevent my subclass from compiling at all with Xcode 4.4 and previous, but that would mean the functionality won't be available on a device running iOS 6.
#interface UIActivityDummy : NSObject
//Copy UIActivity methods to avoid compiler warnings
#end
#implementation UIActivityDummy
#end
#interface MyClass : UIActivityDummy
#end
#implementation MyClass
+ (void) initialize {
Class activityClass = objc_getClass("UIActivity");
if (activityClass) {
class_setSuperclass([MyClass class], activityClass);
}
NSLog(#"%#", [[MyClass class] superclass]);
}
#end
Here, I just elaborating how we can get the class at run-rime. Hope it'll what you needed .Please check it.
just use:
objc_lookUpClass(myClass);// it check for the required class.
objc_getClass(myClass);//IT's what you needed.
Please check the Syntax, as I'm not sure. But, once a time I had used this and was also able to get the ivar and methods and was also able to add method at runtime.
In any concern get back to me. :)

Unrecognized selector error when calling a super's initWithAttributedString: method from an NSAttributedString subclass

I'm tearing my hair out trying to do the simplest of tasks... subclassing an NSAttributedString. Trying to call the super class's initWithAttributedString: method is causing an unrecognized selector sent to instance error.
MODAttributedString.h:
#import <Foundation/Foundation.h>
#interface MODAttributedString : NSAttributedString
#property (nonatomic, retain) NSDictionary *links;
+ (MODAttributedString*) attributedStringWithFormat:(NSString*)text args:(id)argOne, ... NS_REQUIRES_NIL_TERMINATION;
+ (MODAttributedString*) attributedStringWithFormat:(NSString*)text attributes:(NSDictionary*)attributeDict;
#end
The code that is causing the crash (I'll explain the reason I split the alloc from the init in a moment):
MODAttributedString *modString = [MODAttributedString alloc];
// Pausing debugger here and typing 'po modString' causes gdb error
modString = [modString initWithAttributedString:attributedString];
My only clue is that stepping over the alloc call, when I try to po modString, I'm given this error:
"The program being debugged hit an ObjC exception while in a function called from gdb.
If you don't want exception throws to interrupt functions called by gdb
set objc-exceptions-interrupt-hand-call-fns to off.
GDB has restored the context to what it was before the call.
To change this behavior use "set unwindonsignal off"
Evaluation of the expression containing the function (_NSPrintForDebugger) will be abandoned."
If I temporarily change the super class of MODAttributedString to a UIView, the alloc does not cause the gdb error (I stop the debugger prior to the init, which would obviously not work for anything other than an attributed string). However, common classes like NSArray, NSDictionary and NSAttributedString all fail with the same error.
In the method that calls the [MODAttributedString alloc] I use NSAttributedString as its own standalone class just fine. I am sure I'm including the MODAttributedString header in this .m file as well.
I'm using Xcode 4.2 and the iPhone 5 simulator. I've cleaned the project multiple times, tried creating a new project, tried using both LLVM GCC 4.2 and Apple LLVM 3.0, restarted Xcode and restarted my machine all to no success. I searched for this particular issue heavily before posting, but I only found issues related to properties, never to a superclass's public methods.
Is this a build settings issue? A configuration error? A compiler bug? I've subclassed common Apple classes hundreds of times, and for some reason this is the first time I've ever had an issue. Has anyone else ever had a similar problem? It's probably a really simple fix, but I just can't seem to figure it out on my own.
Thanks in advance!
It isn't "the simplest of things". You can't subclass NSAttributedString - it's part of a class cluster. This means (among other things) that the class returned when you instantiate is not necessarily the class you asked for. See What exactly is a so called "Class Cluster" in Objective-C?
It is possible, with great pain and difficulty, to subclass within a class cluster, but my advice is to write a wrapper class instead; you'll be much happier. Cocoa's dynamic redirection of unhandled methods makes this very easy.

XCode skeleton projects overriding

I'm pretty new to Objective-C, so I am not entirely sure of the terms I should be searching for. Apologies if you've seen this question before.
I have noticed that in the skeleton projects that XCode produces contain overrides like so:
- (void)viewDidLoad
{
[super viewDidLoad];
}
I am not sure why this is part of the code that is generated. I am confident that I could omit this method and not affect the application because it simply calls the method on the parent class. Is this method stub just here to show the developer that they can override this commonly overridden method, or is this something in Objective-C that I have not yet come across?
It's a method that you'll often want to override, so it's included in the template as a convenience. If you don't add any code of your own to it, you could as well remove it altogether because only calling super is the same as leaving it out.
I suspect it's also included to remind you that you have to call super if you override it.
Yes, if you have nothing to add to this method it could be left out. It is there to provide you a template where you can add your code for things you want done in viewDidLoad.

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)