Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split Objective-C Code into multiple files

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.

like image 249
Naveed Abbas Avatar asked Nov 13 '12 08:11

Naveed Abbas


4 Answers

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.

like image 188
Kaan Dedeoglu Avatar answered Nov 12 '22 21:11

Kaan Dedeoglu


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 { ... }
like image 41
rob mayoff Avatar answered Nov 12 '22 20:11

rob mayoff


[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
like image 5
software evolved Avatar answered Nov 12 '22 20:11

software evolved


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.

like image 3
justin Avatar answered Nov 12 '22 22:11

justin