将Objective-C代码拆分为多个文件

时间:2012-11-13 08:48:45

标签: objective-c xcode

我经常觉得需要将Objective-C代码拆分成多个文件以提高可读性。我想避免上课并打电话给他们。我想简单导入(比如在php中)。

如果有人可以参考一个有效的例子。

7 个答案:

答案 0 :(得分:13)

你说“我想避免上课并打电话给他们。”你需要克服你的fear of adding classes。如果您觉得需要将类的实现拆分为多个文件,那么您可能会尝试在单个类中执行太多操作。你需要让那个班级(“委托”)将一些责任交给其他班级。

也就是说,有几种方法可以拆分一个类的实现。除了修复膨胀的类设计之外,更好的方法是使用类别或类扩展。您可以在The Objective-C Programming Language中阅读有关类别和扩展程序的所有信息。请注意,链接器会在创建可执行文件时将类别和扩展合并到类中,因此在您自己的类上使用类别或扩展名不会有运行时间损失。

更糟糕的方法是使用C预处理器的#include指令将多个文件粘贴在一起。您可以从实现文件中取出一些方法并将它们粘贴到新的“片段”文件中,然后将#include片段文件放在实现文件中。 这样做会让您更难理解您的源代码。我会建议这样做,但无论如何这是一个例子:

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 { ... }

答案 1 :(得分:9)

我认为你正在研究这种情况下的类别:

您所要做的就是在.h文件中创建一个新的.h .m对:

#import MyClass.h

@interface MyClass(Networking)

//method declarations here

@end

并在.m文件中:

#import MyClass+Networking.h

@implementation MyClass(Networking)

//method definitions here

@end

在MyClass.m文件中 - 执行#import MyClass + Networking.h并完成所有设置。这样你就可以扩展你的课程。

答案 2 :(得分:5)

[编辑/更新 - 我已经放弃了下面描述的方法,支持使用类别,如其他一些答案所述。我的典型情况是,随着视图添加了控件组件,视图控制器文件变得难以处理,因为添加了代码以适应控件的各种委托和数据源方法。现在,我在视图控制器文件中添加代码存根,然后在类类别中实现它们。例如,我可能有一个AlbumViewController屏幕,它有一个搜索栏和一个集合视图,所以我创建了类别AlbumViewController + SearchBar和AlbumViewController + CollectionView。这允许View Controller类保持合理的大小,而不会产生下面列出的包含文件的任何缺点。唯一的缺点是必须公开声明任何实例变量,即属性,才能访问它们。]

我还想分割我的文件,但在某些情况下认为类别不是正确的解决方案。例如,随着nibs增长到包含多个对象(例如表,按钮,导航栏,制表符等),将所有支持方法放在viewcontroller.m文件中的需求可能会导致文件非常大且难以处理。

对于本次讨论,我将引用原始/标准.m文件作为父文件,将子文件.m文件作为子文件引用。

目标是拥有单个父.h和.m,以及多个子.m文件,每个文件都可以独立编辑,但编译好像所有子.m文件都在父.m文件中一样。

如果想要能够将所有与表相关的方法放在一个文件中,编辑它,然后将其编译好就像它包含在viewcontroller.m实现文件中一样,那么这将非常有用。这似乎是可能的,但需要一点努力,并且有一个(可能是严重的)缺点。

要记住的是,使用了两种不同的工具:IDE(提供智能源编辑)和编译/制作系统,它将项目的源代码转换为可运行的应用程序。

要使IDE按预期工作,子文件需要看起来是该类实现的一部分。这可以通过在条件编译宏中包装@implementation和@end指令来实现。当自己编辑文件时,IDE会将子文件视为类的主体,但编译器不会。

为了让编译器不要抱怨,你不能将子文件视为目标的一部分 - 而是通过预处理器#include指令引入它们。这可以通过在创建文件(或添加到项目中)时不将它们添加到目标,或者通过在“构建阶段” - >上删除它们来实现。 “编译源”窗格。

然后#include父.m文件正文中的子.m文件。编译器将它们“就地”加载并根据需要编译源代码而不会抱怨。

这种方法的缺点(到目前为止)是调试器无法识别子方法,并且不会在放在它们上的断点上中断。出于这个原因,我建议这种方法只能在代码经过全面测试后使用,或者用于相对简单且众所周知的代码块,例如表委托和数据源方法。

以下是带有表和nib中的文本字段的项目的.h和.m文件,其中支持委托方法在子.m文件中定义。在nib中,接口对象正常连接,并将委托设置为文件所有者。

文件(父)“MyViewController.h”:

#import <UIKit/UIKit.h>

@interface MyViewController : UIViewController

@property (retain, nonatomic) IBOutlet UITableView *myTable;
@property (retain, nonatomic) IBOutlet UITextField *myTextField;

@end

文件(父)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];
}

文件(子)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

文件(子)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

答案 3 :(得分:3)

这表明你的班级太大了。

一种方法:使用类别。声明通常可以保留在标题中。然后你的实现可能会分裂。只需确保指定您正在实现的类别,因此编译器可以将其与其声明匹配,并在您错过定义时通知您。

答案 4 :(得分:2)

打破大课程的一个好方法是类别中的群组功能(就像Apple在UIViewController中所做的那样)。对于一个大班,我通常将与其相应功能相关的方法和属性分组 在基本头文件中,我拆分了

@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

因为你不能在类别中添加属性(你不能在类别中添加新的iVar和属性不合成)所以你再次在基础扩展的实现中声明它。

@interface SomeClass()
@property (strong, nonatomic) FuncOne *fpOne;
@property (strong, nonatomic) FuncTwo *fpTwo;
@end

@implementation SomeClass
//base class implementation
- (void)someMethodForBase {
}
@end

在SomeClass + FunctionalityOne.m

中实现相应的功能
@implementation SomeClass (FunctionalityOne)
@dynamic fpOne;
//Functionality one implementation
- (void)someMethodForFunctionalityOne {
}
@end

在SomeClass + FunctionalityTwo.m

中实现相应的功能
@implementation SomeClass (FunctionalityTwo)
@dynamic fpTwo;
//Functionality two implementation
- (void)someMethodForFunctionalityTwo {
}
@end

这样我就可以将我的大类实现干净地组织成小类,并将所有类功能信息组合在单个基本标题中以供读取和导入。

答案 5 :(得分:0)

最好在iOS中使用类别,保持代码清洁。

答案 6 :(得分:0)

我知道这是一个旧线程,而Objective C众神将不喜欢这种解决方案,但是如果您需要分割文件,请创建一个新的.m文件并将代码粘贴到那里。然后,在要包含它的.m文件中的@implementation后#include“ yournewfile.m”。避免编译器错误的诀窍是进入构建阶段,然后从“编译源”中删除“ yournewfile.m”。