为 iPhone 开发做好准备
本文献给刚接触 Apple 技术的新手开发者。
引言
这篇文章适合像我一样,之前写过代码但从未为 Apple 技术开发过的人。
一位好朋友送了我一本 iPhone 入门书。于是我安装了 Xcode 并一头扎了进去。这六周过得磕磕绊绊。在我遇到的所有问题还历历在目之际,我将尝试让未来的旅行者走得更轻松些。如果你和我一样缺乏耐心,这本指南就是为你准备的。
我看到很多书籍通过编写自己的 Hello World 应用程序来“重复造轮子”,鼓励读者依赖书籍。这是不好的。Apple 的一些文档非常出色!
资源
- 注册 Apple 开发者(100 美元)
首先,在 Apple 官网上注册成为 Apple 开发者 http://developer.apple.com/。进入后,您将可以访问他们的开发者视频,下载并安装 Xcode,还可以访问他们的论坛。 - IRC -- irc.freenode.net#iphonedev
这个频道非常活跃——提出一个好问题,他们就会帮助你。不过,与任何 IRC 聊天室一样,在没有先检查显而易见的地方就提问,不会让你受到社区欢迎。(如果你没有客户端,可以使用 www.mibbit.com)。 - Stack Overflow
是 Wiki 吗?是论坛吗?不,它是…… Stack Overflow!一项具有革命性意义的新技术,响应速度极快——通常你的问题回答得几乎和在 IRC 上一样快。 - Apple 的文档
……Xcode 会下载并保留本地副本。 - 视频!
斯坦福大学 -- CS 193P iPhone 应用开发
这 20 讲的视频都可以在线观看,虽然不是来自那个网站(但请从那个网站获取源代码!)。你可以在 iTunes -> iTunesU -> ... (然后你需要自己搜索)找到它们。我建议下载所有这些视频,每次有半小时的空闲时间就快速浏览一遍。我喜欢跳到最后部分,那里他用一个示例应用程序将所有内容整合起来,然后如果我对看到的内容感到好奇,就会倒回去看视频。如果我尝试一直看完,我的注意力会分散。WWDC09/10
同样,Apple 开发者网站上的WWDC09/10视频也一样。我建议下载所有内容并快速浏览,让 PowerPoint 幻灯片吸引你的注意力,你就会逐渐构建起自己的知识体系。这些视频主要面向已经熟悉该技术的人,但其中一些是为入门者设计的。 - 善用源代码,卢克
在某个地方创建一个文件夹……/source/samples/,并将大量有价值的源代码下载到其中。Apple 开发者文档示例
Apple 开发者网站上有数百个(100?1000?我忘了)示例。其中一些被列了出来,但有些没有!你只能通过 Google 偶然发现它们,或者在 Xcode 的某个关键词帮助屏幕中找到,然后惊奇地发现它在“相关示例代码”下有内容。WWDC10 源代码
我一时想不起来怎么获取这个——Google 知道!GitHub
同样,有成千上万的项目被人们开源了。不幸的是,没有整洁的方式来浏览这些项目,你最好的办法就是将“iphone”输入搜索字段,然后浏览大约 1000 个条目。我真心希望有人能创建一个组织这些项目的 Wiki,让它们最终变得更容易访问。 - Google 和博客
我推荐一些 Google 搜索技巧来查找入门链接:这里有一个: http://stackoverflow.com/questions/1939/how-to-articles-for-iphone-development-objective-c。一旦你入门了,大约三个月后(我现在就在这个阶段),你可能想开始关注所有顶尖博主并注册 Twitter,这样每当他们发布新文章时你都会收到通知。因为你总会遇到问题,而通常别人已经遇到过同样的问题并解决了,然后写了下来。 http://stackoverflow.com/questions/232570/what-are-the-best-cocoa-touch-iphone-programming-blogs 是一个示例列表。它遗漏了一些很棒的,也包含了一些糟糕的。所以,请自行判断。 - 书籍
另外,如果你是编程新手,你需要按顺序学习:C -> ObjC -> iPhone 编程从书籍上看,这对应着:
《C 语言程序设计》,C 语言的圣经,由 C 语言的作者 Dennis Ritchie 撰写。每个读过它的人都会推荐别人去读。我读了。你也读!
《Objective-C 2.0 编程》 似乎是 Objective-C 的圣经。
《iPhone 开发者食谱:使用 iPhone 3.0 SDK 构建应用程序》,作者 Erica Sadun。
Xcode 的文档 - 设置一些书签
这些文档有点混乱——尽管你安装 Xcode 时它们物理上被安装在你的硬盘上,但 Xcode 却找不到这个版本,而是试图下载最新的帮助文件,大小为 300MB。你可以在 Xcode -> Preferences -> Documentation 中看到这一点。如果你网速慢,那就太糟糕了——这些下载似乎不支持断点续传。我不得不把我的 MacBook 留在了当地的网吧过夜。
让我们花点时间自定义帮助...
在 Xcode 中,选择 Help -> Developer documentation 应该会带你到快速入门页面;给它加上书签,因为它很容易丢失。
尝试找到 Hello World 教程的本地副本。在撰写本文时,这是网页版本:你的第一个 iOS 应用程序。
如果你的大脑像我一样容易分心,你可能会错过顶部的下拉菜单。
你可以在那里看到 Hello World 应用指南的突出显示。给它加上书签。
我建议你也找到 Objective-C 指南,并给它加上书签。请注意,我限制了文档集,否则会显示太多无关内容。在这本入门指南的底部有一个指向完整 Objective-C 参考的链接。如果你是 Objective-C 新手,值得快速浏览一下,这样你就知道在哪里查找奇怪的语法。
作为最后的书签,找到内存管理指南。暂时不用全部读完——先把它准备好,这样当你的代码在两周后崩溃,出现一个完全无用的“bad access”错误时,你就能找到核心规则。
四个书签——现在够了!
一点初步阅读
接下来,我建议你看看快速入门中的视频——这会告诉你如何使用上下文敏感帮助等功能,并快速浏览 Objective-C 指南——如果你不懂 Objective-C,教程会让你感到困惑。如果你是 Objective-C 的新手,你需要知道:它是 C 的扩展。你想写纯 C 代码也可以。但那样你会浪费数千小时的时间来重写 Apple 在其 SDK 中已经写好的绘制文本的类。实际上,你也可以写 C++。你可以将 C 和 C++ 的所有不同方言与 Objective-C 混合使用。
Hello World 教程
接下来,打开 Hello World 教程(我们创建的第二个链接)并完成它。它非常好。在大多数情况下。并非完全如此——它在命名属性的方式上造成了混淆。我已经对此进行了修正——你可以从上面的链接下载我的 Hello World 项目版本。
但在我讨论我所做的更改之前,我想快速提一下配色方案。
配色方案
我建议设置一个好的配色方案,让你能看清各个部分。例如,我将用户定义对象集中在红色、橙色、黄色光谱中,而标准对象则在蓝色、紫色区域。这是我正在使用的截图
你可以看到 `vc` 是我创建的自定义类,显示为红色。`UIWindow` 是 Apple 的类之一。实例变量显示为黄色。
这里可以看到:对于 Apple 的类,紫色方法来自蓝色类。对于我的类,橙色方法来自红色类。 `[vc alloc]` 中的 `vc` 是红色的,因为我创建了这个类,但我继承自 `NSObject`(几乎所有类都应该这样做)。因此,`alloc` 是紫色的——它是 `NSObject` 的一个方法。
随意下载我的配色方案(从上面的链接)并将其放入 *~/Library/Application Support/Xcode/Color Themes*(你可能需要创建一个文件夹),然后在 Xcode -> Preferences -> Fonts and colours -> Colour scheme 中选择它。但我还是建议你先去那里看看你的选项。
属性
我将仅列出两个相关的源文件,因为代码注释非常丰富。我更喜欢在代码中注释,而不是在文章中讨论,这样你就可以直接在代码中逐步阅读,而无需来回查找。简而言之,Apple 的教程通过命名属性的方式造成了双重混淆。我在代码中所做的就是更改名称,从而清晰地区分视图控制器类(`vc`)、该类的应用程序委托实例(`_myVc`)以及应用程序委托用于获取和设置此实例的 getter 和 setter 方法(`myVc`、`setMyVc`)。
//
// wndAppDelegate.h
// wnd
//
// Created by pi on 29/08/2010.
//
@class vc;
@interface wndAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *_window;
vc *_myVc;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) vc *myVc;
@end
//
// wndAppDelegate.m
// wnd
//
// Created by pi on 29/08/2010.
//
#import "wndAppDelegate.h"
#import "vc.h"
@implementation wndAppDelegate
@synthesize window = _window;
@synthesize myVc = _myVc;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// CREATE VIEW CONTROLLER
{
// create an INSTANCE of the vc object/class
// tmp is a temporary pointer pointing to it
vc *tmp = [vc alloc];
/*
note that vc derives from UIViewController
derives from bla derives from NSObject
[vc.h]
@interface vc : UIViewController <UITextFieldDelegate>
Cmd + double-clicking all the way through into NSobject's protocol
reveals retainCount -- ie it keeps track of how many things are holding
onto it, so that it can be garbage-collected when retainCount falls to 0
*/
// as we alloc it, it starts life with a retainCount of 1
NSLog(@"vc *tmp = [vc alloc]; // [tmp retainCount] now: %d",
[tmp retainCount] ); // will be 1
[tmp initWithNibName:@"vc"
bundle:[NSBundle mainBundle]];
/*
when we did '@synthesize myVc = _myVc;' XCode automatically created
two invisible convenience class methods:
... setMyVc (setter)
... myVc (getter)
that get / set the iVar _myVc
NOTE: an iVar is an Instance VARiable -- (lasts only as long
as this instance of the class we're in lasts).
*/
// set using setter method -- EXPLICITLY
[self setMyVc: tmp];
// ... or IMPLICITLY
if (0.0)
self.myVc = tmp; // executes setter: [self setMyVc: tmp];
// Notice in the header, myVc is a RETAINed property
// [wndAppDelegate.h] @property (nonatomic, retain) vc *myVc;
// So, invisibly to us, setMyVc will ++myVc.retainCount to 2
NSLog(@"[self setMyVc: tmp]; // [tmp retainCount] now: %d",
[tmp retainCount] ); // will be 2
// but what we DON'T DO is this:
if (NO+NO+NO*1000)
_myVc = tmp;
// !!! FAILs, as setting the iVar won't ++myVc.retainCount
/*
NOTE: Watch out here -- it is a common coding practice to just do
:
vc* myVc; ...; property (...) vc* myVc; // in the interface, and then
@synthesize myVc; // from the implementation
thus creating setMyVc and myVc setter/getter methods, and a myVc iVar
Yes REALLY DUMB. a myVc getter and a myVc iVar.
MASSIVE CONFUSION POTENTIAL!
There's 3 things:
(1) vc* myVc; // plain old c pointer. a simple iVar -- Instance VARiable --
................ // (lasts only as long as this instance
// of the class we're in lasts).
(2) -void setMyVc:(vc*) ptrToNewVcObject; // invisible setter method
(3) -(vc*) myVc; // invisible getter method
So don't confuse:
(NO) myVc = bla; which just does what it says
on the can - you should NEVER need
................ to do this manually - there is a SETTER method
for setting it
(YES) self.myVc = bla; // which invokes SETTER method,
// which, as myVc is a RETAINED
or [self setMyVc:BLA] // property, increments retainCount
// and sets the iVar (myVc)
LIKEWISE, dont confuse:
(NO) vc* x = myVc;
(YES) vc* x = self.myVc; // or [self myVc]
*/
[tmp release];
/*
If we had just done _myVc = tmp;
then at this point nobody is retaining the object
that both myVc and tmp points to.
It's just going to get garbage-collected
at (or after) the end of the function call.
myVc will be left pointing to empty space & app will crash.
But since we invoked the setter method, thus bringing myVc's retainCount
up to 2, it will persist until we let it go by taking it back to 0 with
[myVc release] in the dealloc method
*/
NSLog(@"[tmp release]; // [tmp retainCount] now: %d",
[tmp retainCount] ); // will be 1
}
// CREATE THE VIEW
// If myVc.view == nil, just the act of accessing it fires myVc.viewDidLoad,
// which should take responsibility for creating a view and setting
// myVc.view to point to it
UIView* myV = self.myVc.view;
[self.window addSubview:myV];
[self.window makeKeyAndVisible];
return YES;
}
- (void)dealloc
{
[self.myVc release];
[self.window release];
[super dealloc];
}
下一步去哪里?
我建议查看登录 Apple 开发者网站时可用的开发者视频。你还可以找到大量的示例源代码——你不必费太大劲就能找到它。