Split Objective-C Code into multiple files - objective-c

I often feel the need to split the Objective-C code into multiple files for better readability. I want to avoid making classes and call them. I want simple import (like in php).
If someone could please refer a working example.

I think you're looking at categories in this case:
All you have to do is to create a new .h .m pair and in the .h file:
#import MyClass.h
#interface MyClass(Networking)
//method declarations here
#end
and in the .m file:
#import MyClass+Networking.h
#implementation MyClass(Networking)
//method definitions here
#end
And in MyClass.m file - do #import MyClass+Networking.h and you're all set. This way you can extend your class.

You say “I want to avoid making classes and call them.” You need to overcome your fear of adding classes. If you feel the need to split a class's implementation into multiple files, chances are you are trying to do too much in a single class. You need to let that class hand off (“delegate”) some responsibilities to other classes.
That said, there are a couple of ways you can split up a class's implementation. The better way, short of fixing your bloated class design, is to use categories or class extensions. You can read all about categories and extensions in The Objective-C Programming Language. Note that the linker will merge the categories and extensions into the class when it creates your executable file, so there's no runtime penalty for using categories or extensions on your own classes.
The worse way is to use the C preprocessor's #include directive to paste multiple files together. You can just take some methods out of the implementation file and stick them in new “fragment” file, then #include the fragment file in the implementation file. Doing this will make it harder to understand your source code. I would not recommend doing this, but here's an example anyway:
MyObject.m
#import "MyObject.h"
#implementation MyObject
- (void)aMethod { ... }
#include "MyObject-moreMethods.m"
#end
MyObject-moreMethods.m
// Note: do not include this target in the “Compile Sources” build phase of your target.
// And **NO** #implementation statement here!
- (void)methodTwo { ... }
- (void)methodThree { ... }

[EDIT/UPDATE -- I have abandoned the approach described below in favor of using categories, as mentioned in some of the other answers. My typical situation is that as a view has control components added, the View Controller file becomes unwieldy as code is added to accommodate the various delegate and data source methods for the controls. Now I add in code stubs in the view controller file, then implement them for real in a class category. For instance, I might have an AlbumViewController screen that has a search bar and a collection view, so I create categories AlbumViewController+SearchBar and AlbumViewController+CollectionView. This allows the View Controller class to stay at a reasonable size, without incurring any of the drawbacks I listed below for included files. The only downside is that any instance variables, ie properties, must be declared publicly for a category to access them.]
I also want to split my files up, but think categories are not the correct solution in some cases. For example, as nibs grow to include multiple objects (such as tables, buttons, nav bars, tabs, etc) the need for placing all the supporting methods in the viewcontroller.m file can lead to a very large and unwieldy file.
For this discussion, I am going to refer to the original/standard .m file as the parent, and the subsidiary .m files as children.
The goal is to have a single parent .h and .m, and multiple child .m files each of which can be edited independently, but compiled as if all the child .m files were in the parent .m file.
This would be useful if one wants the ability to, for instance, place all table related methods in a file, edit it, and then have it compile as if it were included in the viewcontroller.m implementation file. It seems this is possible, but requires a little effort, and has one (possibly serious) drawback.
The thing to keep in mind is that there are two distinct tools being used: the IDE (which provides intelligent source editing) and the compiler/make system which turns a project's source code into a runnable app.
To get the IDE to work as expected, the child files need to appear to be part of the implementation of the class. This can be accomplished by wrapping the #implementation and #end directives in conditional compilation macros. When editing a file on its own, the IDE considers the child files to be the body of the class, but the compiler doesn't.
To get the compiler to not complain, you can't have the child files considered part of the target -- instead they get pulled in through the preprocessor #include directive. This can be accomplished by not adding them to the target when the file is created (or added to the project), or by removing them on the "Build Phases" -> "Compile Sources" pane.
You then #include the child .m files within the body of the parent .m files. The compiler loads them "in place" and compiles the source as wished for without complaints.
The drawback of this approach (so far) is that the debugger does not recognize the child methods, and will not break on breakpoints put on them. For that reason I suggest that this approach only be used either after the code is thoroughly tested, or for code chunks that are relatively trivial and well-known, such as table delegate and data source methods.
Here are the .h and .m files for a project with a table and a text field in a nib, with the supporting delegate methods defined in child .m files. In the nib, the interface objects are wired up normally, and have the delegates set to the file owner.
File (parent) "MyViewController.h":
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController
#property (retain, nonatomic) IBOutlet UITableView *myTable;
#property (retain, nonatomic) IBOutlet UITextField *myTextField;
#end
File (parent) MyViewController.m:
#import "MyViewController.h"
#define VIEW_CONTROLLER_MAIN_BODY 1
#interface MyViewController ()
#end
#implementation MyViewController
#include "MyViewController_TableMethods.m"
#include "MyViewController_TextFieldMethods.m"
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
[_myTable release];
[_myTextField release];
[super dealloc];
}
File (child) MyViewController_TableMethods.m:
#import <UIKit/UIKit.h>
#import "MyViewController.h"
#ifndef VIEW_CONTROLLER_MAIN_BODY
#implementation ViewController
#endif
#pragma mark -
#pragma mark Table View Common Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
static NSString *myIdentifier = #"myCellIdentifier";
static UITableViewCellStyle myStyle = UITableViewCellStyleSubtitle;
UITableViewCell *cell = [self.myTable dequeueReusableCellWithIdentifier:myIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:myStyle reuseIdentifier:myIdentifier] autorelease];
}
cell.textLabel.text = #"Title";
cell.detailTextLabel.text = #"Details";
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
#ifndef VIEW_CONTROLLER_MAIN_BODY
#end
#endif
File (child) MyViewController_TextFieldMethods.m:
#import <UIKit/UIKit.h>
#import "MyViewController.h"
#ifndef VIEW_CONTROLLER_MAIN_BODY
#implementation MyViewController
#endif
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
self.myTextField.text = #"Woo hoo!";
return YES;
}
#ifndef VIEW_CONTROLLER_MAIN_BODY
#end
#endif

This is a good indication that your class is just too large.
One approach: Use categories. Declarations can often stay in the header. Then your implementations may be divided. Just make sure to specify the category you are implementing, so the compiler may match it with its declaration, and inform you when you miss a definition.

A good way to break-down your big class is group functionality in Categories (like Apple does in UIViewController). For a big class, I generally group methods and properties related to their repective functionality and then
In base header file, I split
#interface SomeClass : NSObject
#property (strong, nonatomic) BaseProperty *sp;
- (void)someMethodForBase;
#end
#interface SomeClass (FunctionalityOne)
#property (strong, nonatomic) FuncOne *fpOne;
- (void)someMethodForFunctionalityOne;
#end
#interface SomeClass (FunctionalityTwo)
#property (strong, nonatomic) FuncTwo *fpTwo;
- (void)someMethodForFunctionalityTwo;
#end
Because you can't add property in category (You can't add new iVar and property don't synthesis in category) so you declare it in implementation of base Extension again.
#interface SomeClass()
#property (strong, nonatomic) FuncOne *fpOne;
#property (strong, nonatomic) FuncTwo *fpTwo;
#end
#implementation SomeClass
//base class implementation
- (void)someMethodForBase {
}
#end
Implementing respective functionality in SomeClass+FunctionalityOne.m
#implementation SomeClass (FunctionalityOne)
#dynamic fpOne;
//Functionality one implementation
- (void)someMethodForFunctionalityOne {
}
#end
Implementing respective functionality in SomeClass+FunctionalityTwo.m
#implementation SomeClass (FunctionalityTwo)
#dynamic fpTwo;
//Functionality two implementation
- (void)someMethodForFunctionalityTwo {
}
#end
This way I cleanly organise my big class implementation into small classes and all class functionalities information grouped in single base header for read and import.

its better to use Categories in iOS keep the code clean .

I know this is an old thread and the Objective C gods won't like this solution, but if you need to split files, create a new .m file and paste your code there. Then, #include "yournewfile.m" after #implementation in the .m file where you want to include it. The trick to avoiding a compiler error is to go to the Build Phase and remove "yournewfile.m" from the Compile Sources.

Related

Defining a property in iOS class extension

I would like to add a property to UITableView in a Class Extension:
#interface UITableViewController ()
#property NSString *entityString;
#end
Then I import the extension and then I use entityString property in a subclass of UITableViewController:
#implementation CustomerTableViewController
- (void)viewDidLoad {
self.entityString = #"Customer";
...
[super viewDidLoad];
}
...
Apple documentation says:
the compiler will automatically synthesize the relevant accessor
methods (...) inside the primary class
implementation.
But when I try to execute it I get this error:
-[CustomerTableViewController setEntityString:]: unrecognized selector sent to instance 0x737b670
What am I doing wrong? maybe the property cannot be accessed by subclasses?
Try using a category with Associative References instead. It is much cleaner and will work on all instances of UIButton.
UIButton+Property.h
#import <Foundation/Foundation.h>
#interface UIButton(Property)
#property (nonatomic, retain) NSObject *property;
#end
UIButton+Property.m
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_PROPERTY_KEY;
#dynamic property;
-(void)setProperty:(NSObject *)property
{
objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSObject*)property
{
return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY);
}
#end
//Example usage
#import "UIButton+Property.h"
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button1.property = #"HELLO";
NSLog(#"Property %#", button1.property);
button1.property = nil;
NSLog(#"Property %#", button1.property);
A class extension is used to declare additional interface -- methods and properties -- whose implementation contract will be met within the class's primary #implementation.
Which is exactly why you can't add storage -- add ivars -- via a class extension. A class extension is an interface, no more, no less. #synthesize is what creates storage for #property declarations, but #synthesize of an #property can only appear in the #implementation of the class (whether explicitly or as a default behavior of the compiler).
Since you can't recompile the framework class, you can't add ivars to it.
#prashat's answer is one way to add storage to an existing class. However, going that route is generally undesirable; hanging state off of framework classes willy-nilly is a sign of poor design and will make your application significantly more difficult to maintain over time.
Far better to revisit your design, understand why you currently require attaching state to an object that can't directly contain it, and refactoring that requirement away.
The docs state:
Class extensions are like anonymous categories, except that the methods they declare must be implemented in the main #implementation block for the corresponding class.
When you use #property, it is roughly equivalent to declaring accessor methods. So this means you can only do such a thing if you are also the author of the "main" #implementation block of the class, which with UITableViewController, you are not.
Your only option here is Categories, which cannot add instance variables.
The docs link, and note the very last line of that page:
The implementation of the setValue: method must appear within the main #implementation block for the class (you cannot implement it in a category). If this is not the case, the compiler emits a warning that it cannot find a method definition for setValue:.

Category header imports not working

I have a workspace that has two projects in it. The first project was essentially a test and develop project where I got things working before worrying about tying everything together for real. The second project is bringing all my individually developed view controllers together in a storyboard.
On one of the view controllers I have a bunch of swipe gestures with quite a bit of UIView animation calls nicely formatted for readability and therefore taking a lot of space. I elected to move them out as a category.
The problem is that the compiler is not seeing the instance variable declarations in the main header file.
What has me pulling my hair out is that I did this in the first project and it all worked fine. So I'm carefully comparing the contents of my second project to the first and I see no differences.
Here're some file snippets to help demonstrate how/where I'm defining things, and then snippets of code in the category file that is attempting to access them:
GSBViewController.h
#interface GSBViewController : UIViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *roundPicker;
#property (strong, nonatomic) IBOutlet UIView *roundsSectionView;
GSBViewController.m
#import "GSBViewController+Swipe.h"
#interface GSBGameBuilderViewController ()
{
UIBarButtonItem *rightGatherBarButton;
NSInteger previousRound;
}
#end
#implementation GSBViewController
#synthesize roundPicker;
#synthesize roundsSectionView;
GSBViewController+Swipe.h
#import "GSBViewController.h"
#interface GSBViewController (Swipe)
- (void)establishSwipeGestures;
#end
GSBViewController+Swipe.m
#import "GSBViewController+Swipe.h"
#implementation GSBViewController (Swipe)
- (void)establishSwipeGestures
{
UISwipeGestureRecognizer *swipeLeft =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(roundsSectionLeft:)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[swipeLeft setNumberOfTouchesRequired:1];
[roundsSectionView addGestureRecognizer:swipeLeft];
// bunch-o-code snipped -- for the time being it's actually all commented out
// as a test and because the LLVM compiler was giving up after too many errors
// and I wanted to see if there was more it would like to tell me about this first --
// and very representative -- problem.
}
#end
The complaint from the compiler is "Use of undeclared identifier 'roundsSectionView'"
If I option-click on the use of roundsSectionView in that line of code where I'm adding the gesture recognizer to it the pop-up correctly describes it as declared in GSBViewController.h
So I'm stumped.
Is there something I can do in Xcode (4.3.2 at the time of this posting :-) to let me see what the included files are? Or is there something non-file-based that is needed to tie a category into the class it's augmenting? I don't remember anything like that being necessary before. In fact, the way I generated the files for this category was through Xcode's File -> New File... Objective-C Category template. Then I just copied the contents of the old ...+Swipe.h and ...+Swipe.m files and pasted them into their respective files in the new project.
A synthesized ivar is private. The compiler won't allow you to access it anywhere execept in the #implementation block where it's created. Neither categories nor subclasses can access the ivar directly; they must use the property: [self roundsSectionView].
There's a slight possibilty that earlier Clangs didn't make synthesized ivars private. Either that or you weren't really doing exactly the same thing in the earlier project.
What #Jacques Cousteau says is correct.
Since you just defined a property and no backing ivar, the category won't be able to access it. If you use self.roundsSectionView it will use the getter method generated for the property and hence it will work.
Or you could define a backing variable in your interface
#interface GSBViewController : UIViewController
{
UIBarButtonItem *roundsSectionView;
}
In this case the categories will be able to access the variable. But not any other class.

Objective-C header file not recognizing custom object as a type

I'm working on a game for iPad using cocos2d which involves a board filled with different types of tiles. I've created a custom class called Tile as a general template for tiles and a few subclasses of Tile which have different properties and methods. I've also created a class called Board which, among other things, keeps track of the locations of all the tiles using a special coordinate system.
For some reason, in the Board class, the compiler doesn't seem to be recognizing Tile as a type of object, even though I've added #import "Tile.h" at the top of the file.
Here's the relevant code (just ask if there's other parts of the code you want to see):
Tile.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Board.h"
#interface Tile : NSObject
-(void) updateNeighbors;
#property (nonatomic, retain) CCSprite* sprite;
#property (assign) CGPoint coords;
#property (assign) CGPoint positionInPoints;
#property (nonatomic, retain) NSMutableArray *neighbors;
#end
Board.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Tile.h"
#interface Board : NSObject
+(Board*)sharedBoard;
- (void) putTile: (Tile*) tile AtIndex: (CGPoint) index; //<-- error here!
- (void) replaceTileAtIndex: (CGPoint) index1 WithTileAtIndex: (CGPoint) index2;
- (Tile*) tileAtIndex: (CGPoint) index; //<-- error here!
- (void) populate;
#property (nonatomic, retain) NSMutableArray *tiles;
#property (nonatomic, retain) NSString *type;
#property (assign) CGPoint size;
#end
This code will not even build and I'm getting the following error where indicated:
Expected '(' before 'Tile'
If I change the type from (Tile*) to (NSObject*), it fixes the error, which leads me to believe that Tile is not being recognized as a type of object.
I've searched via Google and this site and cannot figure out why this is happening.
Update
Dumb mistake; easy to fix.
As you all have pointed out the problem is that the two header files are importing each other, which is not allowed. For now, I've fixed the problem by moving the #import "Board.h" statement to Tile.m, since it isn't needed in the header file. Later on, if I decide to use Board in the Tile.h file I will use forward referencing (#class Board;), as a few of you suggested.
Thanks again!
This is a classic problem with headers importing headers. You have a circle here: Tile.h is importing Board.h, which imports Tile.h. This confuses the compiler -- it gets stuck in a loop.
You solve this by not importing headers into headers. You still need to let the compiler know about Tile, however. In Board.h, make a "forward declaration" of the class:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#class Tile; // Dear compiler,
// Tile is a class that I will need to refer
// to in this file. Please consider it to be a
// type; I promise it'll be defined at runtime.
// Sincerely, stephenalexbrowne
#interface Board : NSObject
//etc.
This assures the compiler that there is a class called Tile that will exist at runtime; you can then refer to that name in the remainder of your header. In your implementation for Board, you import Tile.h. That will let the compiler see the methods and properties associated with the Tile class where they are needed.
Likewise, move the #import "Board.h" into Tile.m. Since you aren't referring to the Board class in Tile.h, you don't need to make a forward declaration.
In general, it is best to import your class headers only into the implementation files where they are needed. Framework headers, since they will never cause a cycle with your code, can and -- because you need to refer to many of the classes declared in them -- should be imported into your headers.
Two files cannot import each other. You need to move the import directives to the implementation files, and instead just forward-declare the classes in the headers (e.g. #class Tile; in Board.h).
The reason circular imports don't work is because #import literally includes the text from the imported file in-place. But it also ensures that the text from a file will only be included once, in order to avoid duplicate declarations. So when Tile.h says that the text from Board.h needs to go before it, and Board.h says the text from Tile.h needs to go before it, there's literally nothing the compiler can do — one of them needs to go first, and that file is going to complain because it was expecting the other one to already be there.
This may not be the problem, but what happens if you remove the "#import "Board.h"" from the Tile.h file. You might have a problem with circular referencing

Adding .m source code files for room for long or extra routines

My Xcode .m source files are getting long due to numerous routines, some of which are rather long. I'm looking for the way to add additional .m files and then call the routines in them from my main .m file. I understand the mechanics of adding source files, I really want to know how to call routines in source files other than the one I am currently in.
Example:
mySource.h contains
-(void) myRoutine01;
-(void) myRoutine02; ...
-(void) myRoutine99;
Inside mySource.m I call a routine thusly:
[self myRoutine01];
[self myRoutine02];
[self myRoutine03];
I'd like to keep mySource.m manageable in length such that I am doing mostly logic stuff with calls to routines that are actually housed in other source files. Some routines can get very long and housing them in their own .m files would be most convenient.
How do I call routines in another source file? Do I just replace "self" with something?
What you are calling "routines" are actually methods.
You can organize your methods using Objective-C categories, placed in multiple files.
A category is a way to add methods to existing Objective-C classes. Read the documentation to learn more about this.
This way, you can organize your source files and keep them small, by grouping specific methods into a specific category.
For instance:
MyClass.h
#interface MyClass: NSObject
{}
- ( void )someMethod;
#end
MyClass+Misc.h (category)
#import "MyClass.h"
#interface MyClass( Misc )
- ( void )someOtherMethod;
#end
MyClass.m
#import "MyClass.h"
#import "MyClass+Misc.h"
#implementation MyClass
- ( void )someMethod
{}
#end
MyClass+Misc.m
#import "MyClass+Misc.h"
#implementation MyClass( Misc )
- ( void )someOtherMethod
{}
#end

Putting methods in separate files

I have a class (MyClass) with a lot of methods. Consequently, the .m file has become quite difficult to read. I'm relatively new to Objective-C (having come from REALbasic) and I was wondering if it's possible to put some of the methods in MyClass into different files and then include them in the class. How would I go about this in Xcode?
Yes it is possible and fortunately this can be done easily in Objective-C with Categories.
Say you have your base class MyClass.
#interface MyClass : NSObject
-(void) methodA;
#end
And the according implementation file (not relevant here).
Then you can create a category by defining a new interface in a new header file:
// the category name is in parenthesis, can be anything but must be unique
#interface MyClass (extended)
-(void) methodB;
#end
and the implementation file:
#implementation MyClass (extended)
-(void) methodB {
}
#end
Common convention to name these files is ClassToAddMethodsTo+CatgoryName, i.e.:
MyClass+extended.h
MyClass+extended.m
Group related functionality into categories and give it a meaningful name.
In Objective-c you can break a class into 'categories' - a class spread across many files. The normal Object-Oriented way is to use SuperClasses and SubClasses.
This is almost certainly a code smell telling you that you have a design problem. See this antipattern
There is one thing you could do..........
But be warned, some might consider this pure blasphemy. :)
Say you have a class with two methods you want to have in separate files.
You'll have three files:
• Class.h
• Class.m
• Class_otherMethod.m
Your Class.h should look just like any other. I think it's better to keep the header file complete, but this 'trick' can work on separating .h files just as well.
#interface Class : NSObject
- (void) method;
- (void) otherMethod;
#end
In your Class.m file you will #include the Class_otherMethod.m inside the Class #implementation like this:
#import "Class.h"
#implementation Class
- (void) method {
// do something.
}
#include "Class_otherMethod.m"
#end
And your Class_otherMethod.m file will have only the bare otherMethod implementation:
- (void) otherMethod {
// do something different.
}
Why this works
It's quite simple actually. The preprocessor simply "pastes" the content of Class_otherMethod.m inside the Class.m file and the compiler treats it as one big long file. :P