我经常觉得需要将Objective-C代码拆分成多个文件以提高可读性。我想避免上课并打电话给他们。我想简单导入(比如在php中)。
如果有人可以参考一个有效的例子。
答案 0 :(得分:13)
你说“我想避免上课并打电话给他们。”你需要克服你的fear of adding classes。如果您觉得需要将类的实现拆分为多个文件,那么您可能会尝试在单个类中执行太多操作。你需要让那个班级(“委托”)将一些责任交给其他班级。
也就是说,有几种方法可以拆分一个类的实现。除了修复膨胀的类设计之外,更好的方法是使用类别或类扩展。您可以在The Objective-C Programming Language中阅读有关类别和扩展程序的所有信息。请注意,链接器会在创建可执行文件时将类别和扩展合并到类中,因此在您自己的类上使用类别或扩展名不会有运行时间损失。
更糟糕的方法是使用C预处理器的#include
指令将多个文件粘贴在一起。您可以从实现文件中取出一些方法并将它们粘贴到新的“片段”文件中,然后将#include
片段文件放在实现文件中。 这样做会让您更难理解您的源代码。我会不建议这样做,但无论如何这是一个例子:
#import "MyObject.h"
@implementation MyObject
- (void)aMethod { ... }
#include "MyObject-moreMethods.m"
@end
// 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”。