+load method not getting invoked in iOS? - ios

I am aware that +(void)load method gets called in ios even before the main function.
In my case it is only getting invoked with UIView and not with UITextView.
Are there any files that need to import ?
Does load method work only for certain set of class.
Please provide necessary input regarding conditions required in order to invoke +(void)load method in ios.
Getting invoked here :
#implementation UIView (SomeMethodSwizzling)
+(void)load{
}
#end
Not Getting inovked here .
#implementation UITextView (SomeMethodSwizzling)
+(void)load{
}
#end

Usual reason is that you're not actually linking the file. Most common cause is that you don't directly use anything declared in it (you sometimes need to pass -objc to the linker to fix that). Another cause is you just forgot to add the file to the target (I see that happen a lot).

Related

objective-c ios: override implementation method of class

I'm new to iOS development and a bit stucked with such problem.
In my iphone app I'm using this awesome dropdown view controller https://github.com/nmattisson/DropdownMenu via Cocoapods.
I'm extending DropdownMenuController in my own MyDropdownMenuController:
MyDropdownMenuController.h
#import "DropdownMenuController.h"
#interface MyDropdownMenuController : DropdownMenuController
#end
I would like to override this drawOpenLayer (https://github.com/nmattisson/DropdownMenu/blob/master/DropdownMenu/DropdownMenuController.m#L126) method inside my controller instance, but unfortunately compiler says it's not possible:
MyDropdownMenuController.m
- (void)drawOpenLayer {
// compiler says
// "No visible #interface for "DropdownMenuController" declares the selector "drawOpenLayer"
[super drawOpenLayer];
}
Is it possible to override this method without actually updating external interface etc.?
You can create a category that defines the method
#interface DropdownMenuController (MichaelHacksIt)
- (void)drawOpenLayer;
#end
(no need to define the #implementation for this, because it's already implemented.)
Then you may just call that method.
Disclaimer:
Btw, that's the way to go if you want to call undocumented methods and don't care about Apples approval. In this case, there is nothing wrong with it, because you're not hacking Apple code, and Apple doesn't care if you hack some CocoaPods program. However, you are depending on internals of a third-party package, so there may be problems when you update that package next time...

New to ios protocols are supposed to force you to use methods, but it does not

My app compiles fine even though not all methods are used, here is my protocol:
#protocol azzams_customer_service_delegate <NSObject>
-(void)just_make_dice_appear_in_beginning;
-(void)generate_magic_numbers;
-(void)roll_the_dice;
-(void)check_for_snake_eyes;
-(void)Check_winning_number;
-(void)flash_random_winning_number;
-(void)update_winning_purse;
#required
-(void)u_need_to_im;
#end
I now attach this protocol to this class:
#interface ViewController : UIViewController<azzams_customer_service_delegate>
-(void)just_make_dice_appear_in_beginning;
-(void)generate_magic_numbers;
-(void)roll_the_dice;
-(void)check_for_snake_eyes;
-(void)Check_winning_number;
-(void)flash_random_winning_number;
-(void)update_winning_purse;
#end
notice how the following code is NOT implemented:
#required
-(void)u_need_to_im;
it should give an error, yet my app compiles just fine.
The app will compile just fine, but you should see a warning saying that method is not implemented.
As others have noted, you will see a warning. You can, however, turn on the option in the compiler "Treat Warnings as Errors". Of course, this means you will have to make sure your project is completely free of warnings.
The thing is like - Say class A is the delegate of a protocol and it is supposed to implement a method XYZ. The program will work fine until another class/method tries to access XYZ. If it tries to access the required method, the program will crash.
Example : You implement UiTableViewSourceDelegate and you didn't implement the required methods. But you didn't use a tableview in your viewcontroller. You are on safer side.

NSObject's category is available for every NSObject subclass even without any import of this category's .h file anywhere

Background.
Please consider the following steps:
1) In Xcode create a new "Single View Application".
2) Create a category NSObject+Extension.h and .m files:
// .h
#interface NSObject (Extension)
- (void)someMethod;
#end
// .m
#implementation NSObject (Extension)
- (void)someMethod {
NSLog(#"someMethod was called");
}
#end
3) Ensure that NSObject+Extension.m file is included into a main target.
4) Add the following line to AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[NSString new] performSelector:#selector(someMethod)];
return YES;
}
5) Ensure that #import "NSObject+Extension.h line does not exists anywhere in the app!
6) Run Application.
The output is
2013-08-27 04:12:53.642 Experimental[32263:c07] someMethod was called
Questions
I wonder if there is no any #import of this category anywhere in the app, how is it even possible that NSString does still have NSObject+Extension available? This behavior makes me feeling very bad about every Objective-C category I declare because I want the categories I declare to be available only in the scopes they are declared within. For example, I want NSObject to be extended by Extension only in some class but not in the whole app because its globalspace becomes "polluted" otherwise.
Is there a way to avoid this behavior? I do want my categories to work only when I explicitly import them, not when I just have them linked to a target I use to run.
I wonder if there is no any #import of this category anywhere in the app, how is it even possible that NSString does still have NSObject+Extension available? This behavior makes me feeling very bad about every Objective-C category I declare because I want the categories I declare to be available only in the scopes they are declared within. For example, I want NSObject to be extended by Extension only in some class but not in the whole app because its globalspace becomes "polluted" otherwise.
There are no namespaces on Objective-C objects. If you declare that a class has a method (whether via a category or on the primary #interface) then every instance of that class will have that method.
The way that Objective-C deals with "private" methods is by choosing not to tell other people about the methods in question (which is accomplished by not #import-ing the file that declares those methods). This, coupled with -Wundeclared-selector (warn if you use a selector that the compiler doesn't know about) is about as good of a guard as you're going to get.
But regardless, if you compile the .m file into your final binary, the method will exist, even if no one else "knows" about it.
Are there way to avoid this behavior? I do want my categories to work only when I explicitly import them, not just when I have them linked to a target I use to run.
Yeah, use -Wundeclared-selector, and Xcode will warn you.
Including the header just makes it so the compiler knows about it. It compiles it regardless because xCode compiles every file included in a target. At runtime, the method will be there, so even if you didn't include it for compile time checking, the object will still respond to that category method.

using #implementation creates "instance method conflict" warnings in XCode

In my iOS application I have a 5 view controllers that all deal with the same feature (groups). These view controllers can be pushed on top of eachother in a few different configurations. I made a file called GroupViewHelper.h which uses #implementation to provide some functions for the groups feature. The functions look through the view controller stack and send a "refresh" message to a view controller of a specific type. The file looks like this:
#implementation UIViewController (GroupViewHelper)
- (void) refreshManageGroupsParent
{
// ...
}
- (void) refreshGroupDetailsParent
{
// ...
}
#end
My code works great and everything behaves as expected, but I get 14 warnings that are all very similar to this at build time:
ld: warning: instance method 'refreshGroupDetailsParent' in category from /Users/x/Library/Developer/Xcode/DerivedData/myapp-ayshzmsyeabbgqbbnbiixjhdmqgs/Build/Intermediates/myapp.build/Debug-iphonesimulator/myapp-dev.build/Objects-normal/i386/GroupMembersController.o conflicts with same method from another category
I think I'm getting this because I'm using a .H which is included in multiple places, but how do I correctly use #implementation in this situation?
I think I'm getting this because I'm using a .H which is included in multiple places
Well, sort of, but the real problem is that you've put the #implementation in the .h file in the first place. If you only included that .h file in one place, you would get away with it—but it would still not be the right way to do it.
but how do I correctly use #implementation in this situation?
Put it in a file called GroupViewHelper.m, and add that file to your project's sources, and put the #interface in GroupViewHelper.h.
Or, ideally, call them UIViewController+GroupViewHelper.m and UIViewController+GroupViewHelper.h, because that's the idiomatic way to name category files. (And if you use Xcode's "New File…" menu item to create a new Objective-C category file, that's what it will give you.)
In other words, interfaces and implementations for categories on existing classes work exactly the same as interfaces and implementations for new classes.
I have encountered exactly this issue. I had imported a reference to a header file, on a .m page. However, it also contained a reference to another header file, which contained a reference to another header file - that also referenced the conflicted header file. So indirectly the same header file was imported twice, causing the error.
In my case, the .m file did not need this reference. I was able to delete it, removing the error. My advice is check the files where you have included a reference to the offending header file, and verify that it actually is required.

How to use preprocessor checks in header file?

Okay I know there's a lot of posts on this, but I'm still having trouble. Here's the pseudo code for what I'm trying to do:
if(device is running iOS 5 or up)
#interface RootViewController : UIViewController <UIPageViewControllerDelegate, UIGestureRecognizerDelegate>
#property (strong, nonatomic) UIPageViewController *pageViewController;
else
#interface RootViewController : UIViewController <LeavesViewDelegate, UIGestureRecognizerDelegate>
#property (strong, nonatomic) LeavesViewController *leavesViewController;
endif
Am I right in thinking I need to use pre-processor macro checks since it's in the header file? It's a book app that should use UIPageViewController if it's iOS 5 or up (and therefore has UIPageViewController), otherwise it falls back on Leaves (https://github.com/brow/leaves). I have all the code set up. Just need to know how to tell the compiler which to use. I don't think using any runtime checks would work since I only need the protocol methods for either UIPageViewController or Leaves compiled, not both. And I'd rather not use completely separate source files. I've tried using these checks:
#ifdef kCFCoreFoundationVersionNumber_xxx
#ifdef __IPHONE_xxx
#if __IPHONE_OS_VERSION_MAX_ALLOWED <__IPHONE_xxx
(with various xxx's)
What am I missing here?
EDIT:
I also noticed this in the default .pch:
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
which makes me wonder why that same test didn't work in my .h file?
As I mentioned in the comments, you can't do this at compile time.
But here's an idea for you: It seems that the method names of UIPageViewControllerDelegate and LeavesViewDelegate do not intersect, so you could add the following to your header file:
-(void) leavesView:(LeavesView*)leavesView willTurnToPageAtIndex:(NSUInteger)pageIndex;
-(void) leavesView:(LeavesView*)leavesView didTurnToPageAtIndex:(NSUInteger)pageIndex;
-(void) pageViewController:(UIPageViewController*)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray*)previousViewControllers transitionCompleted:(BOOL)completed;
-(UIPageViewControllerSpineLocation) pageViewController:(UIPageViewController*)pageViewController spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation;
and not explicitly adopt the delegate protocols in the header file (leave out the delegates inside the < >).
Whatever classes you're using for these two could be instantiated in your *.m file in a condition along the lines:
// check for existence of class to determine which controller to instantiate
if(NSClassFromString(#"UIPageViewController"))
{
// do something and set UIPageViewController delegate to "self"
}
else
{
// do something else and set LeavesViewController delegate to "self"
}
Lastly, to get this to compile, you will probably need to forward declare all LeavesViewController- and UIPageViewController-related classes where you use them, and possibly utilize weak linking for some frameworks.
I haven't yet used Apple's UIPageViewController classes and protocols, so I can't provide much more insight than this. Be sure to let us know if you get something hammered out :)
you can't do this, as preprocessor macros are processed at compile-time. How should the compiler know, which iOS you're targeting, since you're compiling on your Mac and not everybody on his iPhone?
You cannot easily switch code at runtime. There are possibilities, but I don't think it is meant like you want it to be.
You can check at runtime if methods are available from specific SDKs. This is much simpler and straightforward. However you cannot achieve your goal with that.
I suggest:
Create a superclass, where you do not have the specific delegate protocols included. There you write all your code, you want to share.
Then create 2 subclasses from upper superclass. In each of the classes put in your specific code.
AND THATS IT.
This is the way it should be.

Resources