Category declaration in *.m file affects whole app - objective-c

I implemented the following category for UINavigationController inside of one *.m file:
#interface UINavigationController (ConfirmPop) <UINavigationBarDelegate>
#end
#implementation UINavigationController (ConfirmPop)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
...
return YES;
}
#end
I was trying to check some conditions before popping the current view controller, and it worked all right but I short noticed that that category affected all UINavigationControllers in my app. Why does that happen? I thought this would only happen if I declared it on a header file and if I imported somewhere - which is not the case.

Categories apply globally. There isn't a way to apply them selectively.
Categories are used to add new functionality to all instances of that class, especially when your code isn't responsible for creating instances of that class -- otherwise subclassing might be a better choice.
You could create your own instance of a UINavigationController with the desired behavior and only use it where you want that behavior. Or if that's somehow not possible, you could add property-like methods to the category that toggle the desired behavior on and off.

Related

Passing information from one view controller to another in XCode using Objective C

To pass information from one view controller to another, I have been using an integer. I declared this integer in one header file, and then set the value of this integer in various .m files through if-then statements. (If the user chooses a certain option, set the value to 1, of another options, set the value to 2.) Based on the current value of the integer, all .m files should know what option the user selected and act accordingly.
This has worked fine until XCode 7. Now I am getting the error "18 duplicate symbols for architecture x86_64" because I used the integer in more than one .m file.
Any suggestions on an alternate way to pass information between view controllers... or how to maintain my current method but get rid of the error... would be greatly appreciated.
Here is some of the code:
#import
int questionTypeInt;
int iPadInt;
int rotationInt;
int iPhoneRotationInt;
#interface QuestionTypeViewController
...
This is my .h file. Then in the .m file for a different view controller:
#import "QuestionTypeViewController.h"
Interface left out for space
#implementation TermsAndPoliciesViewController
-(void)viewDidLoad
{
[super viewDidLoad];
if (iPadInt==0) {
self.view=self.iPhoneTermsView;
} else {
if (rotationInt=90) {
self.view=self.iPadLandscapeTermsView;
} else self.view=self.iPadPortraitTermsView;
}
I use global variables for lots of other things, but they all give me the error.
Wild guess here since we don't have your code.
It sounds like you just made global variables (not in in the #implementation of a class).
If so, they need to have different names if you want them to be different and available outside of the .m. If you want them to represent the same thing, them make one new .m and .h and declare and define it just once.
There's a method in all UIViewControllers called "prepareForSegue", this method let's you send information to the next view controller you're gonna push like this...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIViewController *viewControllerYouWantToMoveOn = segue.destinationViewController;
viewControllerYouWantToMoveOn.info = self.info;
}
Where "info" it's the property that carries the passed data
According to Apple...
Because segues can be triggered from multiple sources, you can use the information in the segue and sender parameters to disambiguate between different logical paths in your app. For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped. You could then use that information to set the data on the destination view controller.
Without more info, I see easy two options:
Make a property on your app delegate. This is frowned upon, and I would avoid this if possible. But...it would work.
Make a singleton class that all your view controllers can access. Because it's a singleton, all your view controllers (and anything else) would be interacting with just one instance.

Is changing the class type back after loading from storyboard dangerous?

Let's say I have a storyboard scene that has a UIViewController in it with its custom class set to ParentViewController. I'd like to create a ChildViewController that does almost everything the same as ParentViewController but customizes just a few behaviors. ParentViewController's init loads the scene from the storyboard.
What I have now is ChildViewController subclassing ParentViewController. When ChildViewController's init calls ParentViewController's init through [super init], the ParentViewController loads the scene which in turn makes my class a ParentViewController even though it was a ChildViewController. After that the ParentViewController's methods get called.
I can get around that by using a strategy pattern, giving the view controller objects that customize the behavior as needed. But the code I'm working in also keys off of the Class types, so I really need a ChildViewController to stay a ChildViewController, or logic down the line does the wrong thing.
Question 1: In this case is it safe to use object_setClass to set the class back to what it was?
Question 2: If I do that do I not need the strategy any more? I assume once the class is set back the proper subclass methods will be called.
I'm just concerned that changing the class when the framework changed it for me in the first place might break something else.

IOS - The correct way for using #import classes

I am writing a test project to learn to write everything in code. I want to do this way so that I can better understand what the Elements in Storyboard do for me behind the scene's
In my project I have several UIViewControllers which share the same UI elements. For Example I have a UITool bar at the top of the screen with several buttons in it.
Now I want to put that UIToolbar in a separate class so that I do not have to write it all out or copy n paste it in every controller.
Now I know I can achieve this by doing this:
#import "General_Add_ons.h" //the UIToolbar is properly set up in this file
#interface FirstViewController : General_Add_ons
and then I just use [self topToolBar]; //topToolBar is the name of the toolbar
Now I just want to clarify is this the best and or only way to this?
Somehow I feel I should be able to do this:
General_Add_ons *gao = [[General_Add_ons alloc] init];
[gao topToolbar];
Am I thinking about this the wrong way? The first way seems to be limiting in case I have multiple external classes.
Thanks in advance.
Your approach seem correct. If you have several UIViewController instances that need the same UI elements or other controller code, you can put those elements in a UIViewController subclass. In your case, I believe the class General_Add_ons is the UIViewController from which your subclasses will inherit.
This class (your General_add_ons) should have a property for the reusable toolbar, e.g.
#property (nonatomic, strong) UIToolbar *toolbar;
As an aside, class names in Cocoa, by convention are: prefix+capitalized words without underscores. Apple has a great reference on naming conventions.
EDIT
For clarification, you can subclass your custom subclass as many times as you need. For example in my code, I have a class CCFViewController that encapsulates common properties and behaviors that I want all of my view controllers to have. In the project, then, all of the view controllers inherit from that parent controller. Similarly, your toolbar will live in the superclass and the other controllers inherit from it.
I am not 100% sure but i think you should try it this way:
General_Add_ons *gao = [[General_Add_ons alloc] init];
[gao.topToolbar anypossiblemethodcall];

Category conflicts

This recent question got me thinking about category conflicts, specifically involving the UIViewControllerRotation category within UIViewController.h. This category includes the shouldAutorotateToInterfaceOrientation:. If one wanted to override this method (via a category) and make it run the same code for every UIViewController (as the linked post is trying to accomplish) then they'd have two categories with the same method - something that I read leads to undefined behavior.
I gave this a try and, for a standard view-based application in iOS 4.3, the rotation logic fell back to the default, portrait only rotation, effectively ignoring the category. This was with no shouldAutorotateToInterfaceOrientation: method defined in my UIViewController subclass. Interestingly, when I did define the shouldAutorotateToInterfaceOrientation: method and simply called return [super shouldAutorotateToInterfaceOrientation:] then the category was called. So this leaves me with two question:
Are all bets off (behavior-wise) when you have conflicting category methods?
Is one out of luck if they wanted to override a category method w/ out inheritance?
Any feedback is much appreciated! Thanks.
Suggestion; don't do that!!!
You really don't want to override behavior of all instances of any given UIKit class across all instances within your application for a couple of reasons.
First, you'll be changing the behavior of the class in ways that the class was likely not designed to deal with. As well, instances that are in UI hierarchies that are beyond your control (embedded complex framework components, typically) will behave differently, too.
Secondly, there is no way your implementation can know the internal implementation details of the original implementation to the point of not risking failing to make the same assumptions. While you could do something swizzle-like, down that path is extremely fragile and will be a maintenance nightmare.
It is undefined what happens if you have conflicting implementations of a category method. UIViewController provides a default implementation of shouldAutorotateToInterfaceOrientation:, so you cannot attach your own implementation via a category.
You may, however, hijack -[UIViewController shouldAutorotateToInterfaceOrientation:] and insert your own implementation. I have a discussion on that in Hijacking with method_exchangeImplementations().
This must be used very carefully, and is dependent on certain implementation details of UIViewController which might change. So I don't recommend this approach for most problems. Generally if you want a "special rotating view controller" that is what subclassing is for. You make your MYSpecialViewController and you subclass from that. Using hijacking (or any other mechanism that inserts itself into the object model dynamically) will impact every view controller in the system, including Apple-provided ones who may or may not react well to it. But for certain problems, it is a very useful solution.
I also do agree with bbum.
However, you can safely use Class Clusters approach. In that way you'll achieve additional benefit: accessing original shouldAutorotateToInterfaceOrientation: method through super.
Implementation may look like the following :
#interface UIViewController (MyViewController)
– (id) initMyControllerWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle;
#end
#implementation UIViewController (MyViewController)
– (id) initMyControllerWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle {
[self release];
return [[InheritedUIViewController alloc] initWithNibName:nibName bundle:nibBundle];
}
#end
Overriding shouldAutorotateToInterfaceOrientation: through simple inheritance :
#interface InheritedUIViewController: UIViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
#end
#implementation InheritedUIViewController: UIViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// some implementation
return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; // optional
//return yourValue;
}
#end
So now, initializing UIViewController through categorized initMyControllerWithNibName::, InheritedUIViewController's shouldAutorotateToInterfaceOrientation: method implementation will be called, with possibility to access its original implementation.

What are NSManagedObjectContext best practices?

I'm working with a Navigation Controller based iOS app. There are multiple tableView screens that pull and save data from a Core Data persistent store. Most of the data for the different table views comes from NSFetchedResultsController instances or NSFetchRequests.
The app works as intended but I have been getting a few random crashes and glitches that seem to be related to Core Data. For example sometimes when I save the context the app will crash but not always. Another thing I've been seeing is the very first tableView doesn't always update the reflect the data that was modified in it's detail view.
Currently I'm passing around a single Managed Object Context that was created in the app delegate to each of the different view controllers by setting the context property of the view controller just before I push it onto the navigation stack.
This seems like a clunky, hacky way of getting the job done. Is there a better design pattern to use?
I noticed in one of the WWDC sessions using delegation but I've never used creating my own delegates before and haven't been able to puzzle it out of the WWDC session.
Thanks.
=)
Use singleton NSManagedObjectContext for all Controllers isn't a best practice.
Each Controller should have your own Context to manage specific, sometimes atomic, operations at document store.
Think if you can edit a NSManagedObject attached to Controller that pass the same Context to other Controller that will select another instance to delete or edit.. you can lost the controll about modified states.
When you create a view controller, you pass it a context. You pass an
existing context, or (in a situation where you want the new controller
to manage a discrete set of edits) a new context that you create for
it. It’s typically the responsibility of the application delegate to
create a context to pass to the first view controller that’s
displayed.
http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/CoreDataSnippets/Articles/stack.html
1)
Use a singleton for your CoreData setup (NSPesistentStoreCoordinator, NSManagedObjectModel & NSManagedObjectContext). You can use this singleton to execute the fetch requests you created in your Models and to add or delete Entities to your Context.
2)
Delegates are not that hard. Following is a sample:
#class SomeClass
#protocol SomeClassDelegate <NSObject> //Implements the NSObject protocol
- (void) someClassInstance:(SomeClass *)obj givesAStringObject:(NSString *)theString;
- (BOOL) someClassInstanceWantsToKnowABooleanValue:(SomeClass *)obj //Call to delegate to get a boolean value
#optional
- (NSString *) thisMethodIsOptional;
#end
#interface SomeClass : NSObject {
id<SomeClassDelegate> delegate;
//Other instance variables omitted.
}
#property (assign) id<SomeClassDelegate> delegate;
#end
#implementation SomeClass
#synthesize delegate;
- (void) someMethodThatShouldNotifyTheDelegate {
NSString *hello = #"Hello";
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(someClassInstance:givesAStringObject:)]) {
[self.delegate someClassInstance:self givesAStringObject:hello];
}
}
#end
Option 1 could be something like this, you will have to setup the variables in the init of the object (and implement the singleton ofcourse):
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface CoreDataUtility : NSObject {
#private
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
+ (CoreDataUtility *)sharedCoreDataUtility;
- (NSEntityDescription *) entityDesctiptionForName:(NSString *)name;
- (NSMutableArray *) executeRequest:(NSFetchRequest *)request;
- (id) getInsertedObjectForEntity:(NSString *)entity;
- (void) deleteAllObjects:(NSString *) entityName;
- (void) deleteManagedObject:(NSManagedObject *)object;
- (void) saveContext;
#end
Currently I'm passing around a single Managed Object Context that was
created in the app delegate to each of the different view
controllers...This seems like a clunky, hacky way of getting the job
done. Is there a better design pattern to use?
There's nothing particularly special about a managed object context in this respect, it's just another object that your view controller may need to do its job. Whenever you're setting up an object to perform a task, there are at least three strategies that you can use:
Give the object everything it needs to get the job done.
Give the object a helper that it can use to make decisions or get additional information.
Build enough knowledge about other parts of the application into the object that it can go get the information it needs.
What you're doing right now sounds like the first strategy, and I'd argue that it's often the best because it makes your view controllers more flexible, less dependant on other parts of the app. By providing the MOC to your view controllers, you leave open the possibility that you might someday use that same view controller with a different context.
Jayallengator makes the helpful observation that every managed object has a reference to its context, and if you're passing around specific managed objects you don't also need to pass along the context. I'd take that a step further: if you're passing specific managed objects to your view controller, the view controller often won't need to know about the context at all. For example, you might keep Game objects in your data store, but a GameBoardViewController will probably only care about the one Game that's being played, and can use that object's interface to get any related objects (Player, Level, etc.). Perhaps these observations can help you streamline your code.
The second strategy is delegation. You'll usually use a protocol when you use delegation, so that your object knows what messages it can send its helper without knowing anything else about the helper. Delegation is a way to introduce a necessary dependency into your code in a limited, well-defined way. For example, UITableView knows that it can send any of the messages defined in the UITableViewDelegate protocol to its delegate, but it doesn't need to know anything else about the delegate. The delegate could be a view controller, or it could be some other kind of object; the table doesn't care. The table's delegate and data source are often the same object, but they don't have to be; again, the table doesn't care.
The third strategy is to use global variables or shared objects (which is what people usually mean when they talk about singletons). Having a shared object that you can access from anywhere in your code is certainly easy, and you don't have that "klunky" extra line of code that configures your object, but it generally means that you're locking your view controllers in to using that shared object and no other. It's a lot like gluing a hammer to your hand because you know for certain that that hammer is the tool you need. Works great for pounding nails, but it can be painful if you later discover that you'd like to use the same hand for driving screws or eating dinner.
The singleton approach seems to be best-practice, but another trick I found useful was that in cases where you're passing a NSManagedObject from one view controller to the next anyway (usually as an instance variable), you don't need to also pass the NSManagedObjectContext since you can get the context from the object you passed in by invoking [myManagedObject managedObjectContext]. This can be a handy shortcut when there's maybe only one or two methods where you need the context and you don't want the overhead of creating yet another NSManagedObjectContext ivar/property.