发布网友 发布时间:2022-04-24 10:07
共1个回答
热心网友 时间:2022-04-08 23:57
Mac OS App 开发并不复杂,XCode 提供了很好的开发环境。但是离开XCode呢?
对于个人开发者,其实 XCode 是一个非常好的 IDE,它有完整的项目组织、代码编辑和浏览、调试、测试、发布功能,并且内置了版本管理支持。但是我仍然有一些理由,去尝试纯手工开发。
如果两个GIT分支分别向同一个项目添加文件,很容易在合并的时候把项目文件搞乱
有时候我们希望快速建立一个原型,XCode够快,但是如果能基于纯文本建立一个模板系统,就更快了
你在开发 APP 的时候升级过你的mac port、homebrew、fink之类的工具吗?我做过……
nib 文件的可视化设计和MVC模式非常漂亮,不愧是 Smalltalk 血统。但是有时候我们希望能从编码的角度审视设计
如果需要把项目分发给别人使用,例如开源项目;或者需要无人值守的测试、集成等工作,基于脚本要方便的多
就是想知道项目构建的每个细节
……
还需要更多的理由吗?那我再加一个:我喜欢Emacs……
所以,这里我们会通过几个简单的例子,讨论一下如何纯手工开发 Mac OS App。
main 函数中的故事
最简结构
默认使用 Objective C 这个前提下,最简单的mac 程序,我甚至可以默写:
//simple.m
#import <Foundation/Foundation.h>
int main(int argc, const char *argv[]) {
return 0;
}
这个程序用clang可以直接编译,不过它什么功能也没有。我们直接跳过 Hello World什么的,看下一步。
app 程序的简单结构
我们看一下XCode生成的项目的话,会发现 main.m 简单到离谱:
//simple.m
#import <Cocoa/Cocoa.h>
int main(int argc, const char* argv[]) {
return NSApplcationMain(argc, (const char**)argv);
}
这次,编译的时候,你需要加上framework:
clang -framework Cocoa -o simple simple.m
好的,这次编译过了,也生成了二进制文件,但是如果你直接执行 ./simple,会发现系统报错给你看(我用的 Mountain Lion)。
这是因为我们缺少一些配置信息,这个问题我们后面讨论,暂时我们先继续研究如何建立一个 app。
最简单的 app 很容易构造,我们随便打开一个 app (右键,然后选“查看包内容”),就可以看到它的结构,招方抓药:
建立 simple.app/Contents/MacOS 目录
把编译出来的可执行文件 simple 复制进去
然后,你就可以执行 open simple.app 运行这个app了。
做点事情
我在 https://github.com/Dwarfartisan/BlackCookbook/tree/master/objc 放了几个示例程序,现在大家可以先看 noxcode ,这个项目很简单。
首先,你需要一个继承自 NSWindow 的新 window 类型,其实我们仅仅是需要重载它的 canBecomeKeyWindow 方法。
这个示例是我按照 http://forums.macnn.com/t/209595/cocoa-without-nib-file-need-help 写的,改了一些东西,所以类型名按原例定为 myWindow,头文件里没什么特别的东西,.m 里也只需要一个定义:
#import "myWindow.h"
@implementation myWindow
-(BOOL) canBecomeKeyWindow {
return YES;
}
@end
其实,noxcode项目的代码可以精简成只有 myWindow 和这样一个 main.m:
#import <Cocoa/Cocoa.h>
#import "myWindow.h"
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSWindow *window = [[myWindow alloc] initWithContentRect:NSMakeRect(50, 100, 200, 300)
styleMask:NSTitledWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:YES];
NSTextField *text=[[NSTextField alloc] initWithFrame:NSMakeRect(10, 60, 180, 32)];
text.stringValue = @"sample text";
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(10, 10, 180, 32)];
[button setBezelStyle:NSRoundedBezelStyle];
[button setTitle:@"Quit"];
[button setTarget:NSApp];
[button setAction:@selector(terminate:)];
[window setTitle:@"test1"];
[[window contentView] addSubview:text];
[[window contentView] addSubview:button];
[NSApplication sharedApplication];
[window makeKeyAndOrderFront:nil];
[NSApp run];
}
return 0;
}
原示例中还有个 myView ,是原作者演示自定义view的,可以去掉,这样我们就有了一个带窗口的app。
然后你可以手工编译它,自己建立对应的app包,也可以用这样一个 Makefile:
CC=clang
BUILD=$(CC) -fobjc-arc
LINK=$(BUILD) -framework Cocoa
.PHONY: all run clean
all: build
mkdir -p mytest.app/Contents/MacOS
cp mytest mytest.app/Contents/MacOS/
run: all
open mytest.app
build: myWindow.o main.o
$(LINK) -o mytest myWindow.o main.o
myWindow.o:
$(BUILD) -c myWindow.m
main.o:
$(BUILD) -c main.m
clean:
-rm mytest myWindow.o main.o
-rm *~
-rm -r mytest.app
Makefile 的详细使用方法不多解释了,这个东西我确实也不是内行,只是看了一下教程然后写来图省事的……
略进一步
Congratulations ! 我们有了带窗口的 app 。但是很多程序在启动的时候,并没有一个初始窗口。我们接下来构造两种常见的 app ,一种带有主菜单,一种带有状态栏菜单。
带主菜单的 app
完整的项目示例在这里:
https://github.com/Dwarfartisan/BlackCookbook/tree/master/objc/mainmenu
这里我们自定义了一个 MainMenu 类型,主要是为了把菜单结构的构造封装起来,跳过这一步,我们先看 main.m :
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
id delegate = [[AppDelegate alloc] init];
app.delegate = delegate;
return NSApplicationMain(argc, (const char**)argv);
}
}
这里跟以前的例子不同的是,我构造了一个 app delegate 结构的应用,实际的 GUI 拼装过程是从 delegate 内部进行的。另外,上个例子中有个 [NSApp run],这很关键。它是 Cocoa 程序的事件循环。如果没有它,我们需要一个 Info.plist ,告诉系统启动 app 的时候,如何找到 NSPrincipalClass 。 在这个项目的代码库中,我们可以找到这个Info.plist :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
Info.plist 还可以描述很多非常有用的信息,例如设定app不在dock上显示图标。这个可以查阅 apple 的官方文档或者 google,不多讨论了。在 Makefile 里,我把它复制到了app包对应的位置。
我们看看关键的 AppDelegate.h :
/* -*- mode:objc -*- */
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
-(IBAction) quit:(id)sender;
@end