iPhone和iOS开发入门(译文)
By S.F.
本文链接 https://www.kyfws.com/news/getting-started-with-iphone-and-ios-development/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 29 分钟阅读 - 14040 个词 阅读量 0iPhone和iOS开发入门(译文)
原文地址:https://www.codeproject.com/Articles/88929/Getting-Started-with-iPhone-and-iOS-Development
原文作者:Joel Ivory Johnson
译文由本站翻译
前言
这是一系列有关iPhone开发并开始运行的文章的第一篇.第一篇文章旨在帮助您确定开发所需的硬件,并快速介绍Objective-C.
介绍
这是一系列有关iOS开发并开始运行的文章的第一篇. iOS是在iPhone,iPad和iPod Touch上运行的操作系统.虽然我将使用iPhone作为示例代码,但是您可以将代码应用于所有三个设备.在熟悉iOS开发之后,请看本系列的第二篇文章:iOS图形API简介第1部分.
先决条件
要利用本文,您需要已经对面向对象的概念有所了解,并且需要了解一种C语言(C,C ++,C#),并且知道当我使用诸如对象,类,函数,方法,变量等.我假设您从未看过Objective-C.我将向您介绍Objective-C语言,然后引导您完成一些我认为最能帮助您入门的编程方案.就像在任何编程环境中一样,完成任务的方式不只一种,或者表达某种东西的语法也不止一种.穷举呈现某种可能表达某种事物的方式是不可能的.因此请记住,还有其他方法可以完成我在此处解释的内容.
选择硬件
iOS应用程序的最低限度开发环境仅由基于Intel的Mac组成.拥有iPhone,iPod或iPad是可选的,但非常有帮助. 如果您正在寻找用于iOS开发的绝对最佳硬件,可以很容易地说"使用所有可能的选项使Power Mac成为顶级产品".但是对于我们大多数人(包括我自己),在选择您使用的开发硬件时会受到成本限制.您所需的绝对最低硬件配置是运行Snow Leopard的基于Intel的Mac.可以购买的最便宜的Mac是Mac Mini.与您可以购买的成本最低的PC的价格相比,Mac Mini看起来非常昂贵.但是我建议您多花一些钱来买一台便携式计算机.使用Mac Mini,您将只能在设置房间的房间内进行开发.使用其中一本Mac图书,当您在电视前或其他舒适的地方放松时,您可能还会发现自己正在发展. 根据您的需求,如果您发现不需要用于预定应用程序的最新版本的iPhone,则当Apple发布新iPhone或新Mac时,货比三家是个好主意.我能够从正在升级的其他人那里购买到便宜的iPhone,然后从一家商店出售了Mac Mini,该商店正在清理货架以购买更新的产品. (从那以后,在希望能够从房子的其他房间进行开发之后,我便开始使用Mac Book Pro.)
您无需拥有iPhone,iPod或iPad即可上手(在此,我将这些设备统称为iOS设备). SDK随附有模拟器(如果您习惯使用术语,则可以使用模拟器).但是模拟器有局限性.您会发现在开发使用加速度计的任何产品时都希望拥有真正的硬件. 如果您打算使用真实的硬件进行测试,仅拥有所需的硬件和软件是不够的.您还需要订阅Apple的iPhone开发计划(每年约99美元).
我需要一台Macintosh吗?
我审查了一项旨在使没有Mac的用户可以进行iPhone开发的服务,因此我的个人网站获得了大量访问量.我不想在这里讨论该服务,所以我只想告诉你,必须安装Macintosh.用于iPhone开发的唯一经认可的硬件是基于Intel的Macintosh.不要浪费您的时间来规避这一点.
安装SDK
iPhone SDK已经存在于OS X Snow Leopard的安装盘上.不要使用那个.始终会有更新的版本可供下载.大约需要3份演出,SDK可能需要一些时间才能下载,具体取决于您的连接速度.要获得安装,请转到http://developer.apple.com.您会看到一个指向iPhone开发人员中心的链接.单击它将带您到下载SDK的页面.苹果有时会提供SDK的Beta版本供下载.但是与当前版本的SDK不同,beta版本并非可供所有人下载.您必须是具有订阅的注册开发人员才能访问它们. SDK的DMG将下载并自动安装.运行安装,安装完成后,您就可以开始进行开发了.
Objective-C入门
为iPhone创建的程序是用Objective-C编写的. Objective-C通常被描述为C语言的严格超集.其他人将其称为C,并应用了面向对象的概念.第一次看到它时,我不得不说我感觉自己不知道自己在看什么.它看起来不像我所知道的任何其他编程语言.但是,在完成了前几个程序之后,您会发现它变得更容易遵循.作为C的严格超集,如果您已经有用C编写的算法,则应该能够将其移植到iPhone.下表显示了一些表达式,这些表达式在C和Objective-C中的含义几乎相同: 在Objective-C中,您的类定义和从这些类实例化的对象都是名为" id"的结构的实例. id的定义如下:
typedef struct objc_object {
Class* isa;
} *id;
由于这是Objective-C中所有对象的基础,因此您将在每个引用其类型数据的Objective-C对象上找到一个"ʻisa"成员. "
id"实例都是指针,因此您可以通过比较它们的地址来区分它们.有一个名为" nil"的特殊" id"值,其指针值设置为零.
班级
在Objective-C中,您可以将类的接口与其实现分开声明.您可能已经在C ++程序中看到接口和实现的类似分离,其中在* .h文件中定义了一个类的接口,而在* .cpp文件中定义了该类的接口.同样,在Objective-C中,接口在* .h文件中定义,而其实现在* .m文件中. 您在Objective-C中创建的每个类都应直接或间接源自NSObject基类.乍一看,基类看起来并没有做什么工作,但它包含了运行时与对象进行交互所需的功能.您不需要自己实现此功能.
类接口声明
要声明一个新的类,首先要声明该类的接口.类接口声明以@interface编译器指令开头,以@end指令结束.您将在类接口声明中看到一组打开和关闭的花括号.只需记住,右括号不是类接口定义的结尾!这是一个通用的类定义:
@interface YourClassName: TheSuperClass
{
instance members
}
Message Declarations
@end
实例成员的声明与其他C语言之一所期望的声明没有不同.
BOOL isValid;
float width;
NSString* name;
一个类自动访问其继承祖先中的所有类.因此,您可以引用其任何基本类型的类,而无需执行任何特殊操作.如果引用的类不在其继承链中,则需要对该类进行前向声明.以下内容对两个类进行了前向引用:“雇员"和"会计”.
@class Employee, Accountant;
消息的声明是不同的.消息可以与该类关联,也可以与该类的实例关联.类方法的前面加号(+),而实例方法的前面加减号(-).类消息的默认返回类型是id实例.要将返回类型更改为其他类型,您需要在类名称之前的括号中添加一个返回类型.请注意,这与将变量从一种类型转换为另一种类型的语法相同.这是一个简单的消息声明的示例:
- (int)GetSize;
消息参数紧随消息名称之后.它们用冒号分隔,并且用与将其强制转换为另一种类型时相同的语法来指示其类型.
-(float) Power:(float)width:(float)height;
类实施
该类的实现位于* .m文件中.就像在C ++中一样,编写实现时,您将需要包含类的头文件.而不是使用#include指令来执行此操作,而是使用#import指令.声明实现的语法类似于声明接口的语法.只有您将@ interface
指令替换为@ implementation
指令.开始实现的一种可能语法如下:
#import "YourClass.h"
@implementation YourClass
your message implementations go here
@end
(可选)您可以在此处重新声明实例变量和超类,但这不是必需的.消息实现的开始方式与消息相同.但是,您不必使用分号(;
)终止声明,而是可以使用大括号对包围整个实现.
- (float)square:(float)x
{
return x*x;
}
方法,消息和对象通信
您遇到的大多数面向对象的材料都是指在对象之间发送的消息.大多数时候,您会看到这是通过方法实现的.在大多数情况下,方法和消息一词可以互换使用. Objective-C坚持使用术语"消息".因此,我将在本文档中遵守该标准. 发送消息的语法(与"调用方法"含义相同)是将目标对象的名称和要传递的消息放在方括号内.因此,在C ++/C#中,您可能已经使用以下语法发送了一条消息:
myObject.SomeMethod();
在Objective-C中,您将通过以下操作完成相同的操作:
[myObject SomeMethod];
将参数传递给消息与您习惯的有所不同.对于一个参数,事情很有意义,不需要太多解释.只需在消息名称后加上参数值后加一个冒号.
[myObject SomeMethod:20];
当您想传递多个参数时,事情可能看起来很混乱.
[myObject SomeMethod:20:40];
随着更多的参数添加到消息中,代码可能会变得可读性降低.我不希望开发人员记住方法需要的参数.即使他或她做到了,仍然需要花费精力来确定与哪个值相关联的参数.为了清楚起见,您可以执行以下操作:
[myObject SomeMethodA:20 B:40];
在上面的代码中,似乎我已将参数命名为" A"和" B",并且将参数" A"奇怪地连接到消息名称.您可能还没有意识到,参数名称和冒号是消息名称的全部部分.因此,消息的全名是SomeMethodA:B:
.
如果消息将对象作为消息返回,则可以通过嵌套调用将消息传递给返回的消息.在C/C#中,您将具有如下代码:
myObject.SomeMessage().SomeOtherMessage();
而在Objective-C中,可以用以下方法完成相同的事情:
[[myObject SomeMessage] SomeOtherMessage];
样机
原型很像弱实现的接口.在其他编程语言中,如果一个类实现了一个接口,则在该类的声明中,有一条语句表明该类正在实现某个接口.对于原型,类仅需要实现某些方法以符合原型.不需要提及它所遵循的原型定义.
类的实例化和初始化
使用该类的alloc方法实例化一个类的实例. alloc将保留该类所需的内存.分配内存后,您将需要对其进行初始化.每个具有实例成员的类都需要定义一个" init"方法.
myObject = [[MyClass alloc] init];
第一次看到上述模式时,我误以为它等同于以下内容:
myObject = [MyClass alloc];
[myObject init];
它们不相等. " init"返回的值可能与" alloc"返回的值不同.因此,在进行分配时,应始终结合使用alloc和init消息. 当您为一个类实现自己的初始化程序时,初始化程序应返回" self"表示成功,或返回" nil"表示不成功.初始化程序的返回类型应始终为" id".这是为了明确表明初始化程序返回的类型可能是另一种类型. 如果您需要将参数传递给您的"初始化"解析器,则通常会将" init"作为"初始化"解析器名称的前缀.例如,如果我有一个需要日期的" initialializer",我可以将其称为" init`WithNSSDate".
内存管理
Objective-C类跟踪引用计数.可以在任何时候通过-(NSUInteger)retainCount;
读取计数(尽管除了满足好奇心外,没有很多需要这样做的场景).当一个对象的引用计数达到零时,它将被释放.在大多数情况下,此引用计数的详细信息已被抽象化,与引用计数相关的方法被呈现为获取对象所有权(增加引用计数)或放弃对象所有权(减少引用计数)的方法. ).
一般规则是,如果您使用任何带有前缀alloc或new的方法创建了对象,或者已经从现有对象复制了对象,则您有责任释放该对象.如果您已经拥有对象的所有权(带有"保留"),那么您还必须"释放"它.注意:如果您不拥有它,请不要"释放"它.在对象上调用autorelease
会将其添加到对象池中,以便将来在某个时候释放和分配.
复制对象
复制对象时,如果该对象的成员变量包含指向其他对象的指针,则有两种方法可以复制它.一种方法是将指针从原始对象复制到正在创建的对象.这种复制方法(也称为浅表复制)将导致新对象和原始对象共享组成其成员的对象的相同实例.因此,如果一个类的成员对象中发生更改,由于它们实际上是同一实例,因此也会影响另一类中的成员.复制的另一种方法是在创建新对象时创建成员对象的新实例.这样做的最终结果是,在一个对象的一个实例上更改该实例对象不会对另一个实例产生影响.在这种情况下,原始对象和复制对象是完全独立的.当从内存消耗和潜在副作用方面看两种复制方法的结果时,这两种方法都有其优点和缺点.可能存在混合使用浅层复制技术和深层复制技术的方案.如果您创建了一个类并实现了深层副本,但是其中一个实例成员实现了浅层副本,则最终可能会导致实例成员的实例成员与该对象中的实例成员的实例成员引用同一对象您刚刚复制(这可能会造成混淆,但是我认为没有更清晰的说法). 类的复制方法如何工作与类的set方法如何工作之间应该保持一致.如果类上的set方法在执行分配时创建了新实例,则copy方法应该是深层副本.如果set方法保存了传递给它的实例,则copy方法应该是浅表副本.
低内存条件
iOS程序的用户界面将通过从" UIViewController"派生的类进行控制.低内存处理涉及该类上的两种方法.一种是已存在的didReceiveMemoryWarning在iOS 3之前.从iOS 3及更高版本开始,有一种名为viewDidUnload.当设备内存不足时,如果操作系统知道可以重新加载并重新创建视图,则操作系统将销毁这些视图.调用此方法时,您的类应通过释放其对视图对象的引用来进行响应.
建构Hello World
如果您跳过了Objective-C的介绍,而是试图直接创建一个程序,那么您将迷失在神秘的符号和无意义的字形中.我已经阅读了入门文章,然后您便可以理解接下来的内容.我们将建立一个" Hello World"程序.该程序将具有一个按钮和一个文本字段.当有人单击该按钮时,文本字段将显示单词" Hello World".我还将以此为契机,介绍一些如果没有本练习就无法理解的概念. 使用Finder或Spotlight启动Xcode.当出现Xcode欢迎屏幕时,选择选项"创建新的Xcode项目".
确保在iPhone OS下选择了"应用程序"以查看可以创建的iOS项目类型的列表.选择"基于视图的应用程序",并确保在"产品"下拉列表中选择了iPhone.完成此操作后,单击"选择"按钮,并在提示您输入项目名称时键入" MyHelloWorld".
生成一个新项目,并且此时该项目处于可以运行的状态.确保您的iOS设备未连接到计算机(如果您现在尝试部署到自己的个人设备,它将失败),然后选择"构建并运行".您的项目将被编译并复制到仿真器中并运行.结果看起来很无聊.这只是一个空白屏幕,因为我们尚未向用户界面添加任何内容.您可以单击Xcode中的红色按钮以停止该程序.该程序终止时,您将在iPhone模拟器中看到其图标.它只是一个纯白色的圆角正方形.让我们改变它. 如果要更改程序的图标,请制作一个新的57x57像素的PNG图片.不必担心拐角处或做其他使它看起来像iPhone风格的事情;这将自动为您发生.在选择的编辑器中制作完图像后,请在Finder中导航至该图像.单击图标并将其拖到Xcode中的Resources文件夹中.系统将询问您是否要将文件复制到项目的文件夹中,或者只复制参考.通常,复制文件是一个好主意. 在资源文件夹中,找到文件HelloWorld-Info.plist并单击它. * .plist文件是一个属性列表,其中包含有关该应用程序的一些常规信息.单击plist文件将导致属性编辑器打开.可用属性之一称为"图标".双击该属性以开始对其进行编辑,然后键入图标文件的名称.关闭应用程序后,下次生成并运行该应用程序时,将看到您的图标. Xcode窗口分为三个窗格.顶部的水平窗格中包含文件列表.查找名为MyHelloWorldViewController.XIB的文件,然后双击它.这将打开界面构建器.
在其他窗口中,您应该会看到一个空白的iPhone表面,可以在其上布置控件.如果看不到,请在出现的一个显示布局表面的窗口中双击"查看"图标.在"库"窗口中向下滚动,直到看到"文本字段"控件.将此控件的一个实例拖到屏幕上半部分的设计图面上.另外,将"圆角矩形按钮"拖到靠近文本字段的设计图面上. 选择按钮,然后转到属性窗口中的"身份"选项卡.将按钮的名称设置为" MyButton".选择文本字段并将其名称设置为" MyText".保存所做的更改,然后返回Xcode并再次运行您的项目.这次,您将在屏幕上看到文本字段和按钮.
我们想要更改程序,以便当用户单击按钮时,在文本字段中显示文本" Hello World".但是,如果您仔细阅读我们的源代码,将会发现按钮和文本字段都无法立即供我们在代码中进行操作.
IBAction和IBOutlet-响应动作并提供反馈
单击按钮后,我们可以使按钮向我们的代码发送消息.我们需要在代码中声明一个" IBMethod",以便按钮可以向其发送消息.打开" HelloWorldViewController.h",然后在接口定义的右括号之后的行中添加以下内容.
- (IBAction) myButtonWasClicked:(id)sender;
通过阅读Objective-C入门可能已经积累了一些知识,您还需要在HelloWorldViewController.m文件中为此方法添加一个实现.在@implementation指令之后的行中,键入以下内容:
- (IBAction) myButtonWasClicked:(id)sender
{
NSLog(@"The button was clicked");
}
在界面构建器中打开HelloWorldViewController.xib.按住控制键,然后单击并拖动鼠标到"文件的所有者"图标.出现一个子菜单,允许您选择一种方法.选择" myButtonWasClicked"方法. 运行项目,然后在Xcode中键入Shift-Command-R打开控制台.每当您单击按钮时,都会看到文本"按钮被单击"出现. 我们的最终目标是在屏幕上向用户显示一条消息.还有更多的工作要做.尽管IBAction方法使我们能够接收到某些事件的通知,但它们在主动更改文本字段的内容方面无济于事.我们首先需要获得文本字段的实例.但文本字段未在我们的代码中声明.它在XIB文件中声明.要获得对文本字段的引用,我们必须声明一个出口.出口是一个变量,可可在运行时会自动为其分配一个值. 在视图的头文件中,在您创建的方法下键入以下内容:
@property (retain) IBOutlet UITextField *myTextBox;
将相同的内容复制到实例变量的区域中,然后从其中删除" @property(保留)“部分. 在实现中,添加以下内容:
@synthasize myTextBox;
myButtonWasClicked方法中的代码也需要更改.在此方法中,将” myTextBox"实例上的文本设置为" Hello World".
myTextButton.text=@"Hello World"
下一步是将文本字段与此接口构建器中的此属性相关联.在界面构建器中,右键单击"文件的所有者"模块,然后找到" myTextBox"出口.按住控制键的同时,从插座中单击并拖动到文本字段.如果现在运行该程序,则在单击按钮时," Hello World"将出现在文本框中.我们只看了文本框.但是与许多其他视觉控件的交互是相同的.如果在界面上添加一个滑块,在类中添加一个IBAction方法,然后将该方法与该滑块相关联,则当滑块值更改时,我会收到通知.
最后一步是清理任务.如低内存部分中所述,如果iPhone的内存不足,它将开始破坏视图以清除内存.发生这种情况时,我们将需要清除对视图对象的引用.同样,在重新分配视图时,我们需要释放对代码中引用的视图对象的所有权.您可以在下面完整代码的-(void)viewDidUnload
和-(void)dealloc
方法中看到这两种方法是如何实现的.
下面的源代码包含HelloWorld功能以及显示滑块的值.
#import <uikit>
@class NSTextField;
@interface HelloWorldViewController : UIViewController {
IBOutlet UITextField * myTextBox;
IBOutlet UISlider * mySlider;
IBOutlet UITextField * mySliderText;
}
@property (retain) IBOutlet UITextField * myTextBox;
@property (retain) IBOutlet UISlider * mySlider;
@property (retain) IBOutlet UITextField *mySliderText;
- (IBAction) myButtonWasClicked:(id)sender;
- (IBAction) mySliderWasMoved:(id)sender;
@end
#import "HelloWorldFinalViewController.h"
@implementation HelloWorldFinalViewController
@synthesize myTextBox;
@synthesize mySliderText;
@synthesize mySlider;
- (IBAction) myButtonWasClicked:(id)sender
{
NSLog(@"The button was clicked");
myTextBox.text=@"Hello World";
}
- (IBAction) mySliderWasMoved:(id)sender
{
NSLog(@"The Slider was moved");
UISlider *slider = (UISlider*)sender;
float sliderValue = [slider value];
NSString* sliderValueText =
[NSString stringWithFormat:@"The slider's current value is %f",
sliderValue];
mySliderText.text=sliderValueText;
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
//View is being destroyed to free memory. Clear
//out our references to view obejcts.
- (void)viewDidUnload {
self.textBox=nil;
self.mySliderText=nil;
self.mySlider=nil;
}
// during a dealloc release our hold on the view objects
-(void)dealloc{
[myTextBox release];
[mySliderText release];
[mySlider release];
[super dealloc];
}
@end
此示例程序要采取的两件事是如何获取对在界面生成器中创建的UI对象的引用,以及如何从UI对象接收消息.
部署到真实设备
既然您已经编写了第一个程序,那么拥有真实设备的人可能想要将其部署到该设备上.在将程序复制到真实设备上之前,您必须在Apple进行Appstore注册.在执行此操作之前,您将无法完成设置过程. 将iOS设备连接到计算机.在Xcode中,更改下拉列表,以使Xcode可以在实际设备上运行该程序,而不是尝试在模拟器中运行该程序.当您尝试运行该程序时,会收到一条失败通知,指出该设备没有配置文件.如果从错误对话框中选择"安装并运行",则会遇到有关找不到配置文件的另一个故障. 要配置设备,您需要拥有开发者证书.转到iPhone开发人员门户(http://developer.apple.com/iPhone)并使用您的Apple ID登录.在左侧,有一个链接会将您带到供应页面.单击链接,然后从页面底部选择"启动助手".当Development Provisioning Assistant的欢迎窗口打开时,单击" Continue".在下一个屏幕上,您将必须标识正在开发的应用程序.单击"创建新的应用程序ID",然后选择"继续".在下一个字符串上,输入应用程序的描述或名称(很有创意!).之后,您必须确定用于开发的Apple设备.选择"分配新的Apple设备".系统将要求您提供设备的描述和设备ID. 描述几乎可以是任何名称.如果只有一台iOS设备,那么您实际上将不会在意其中的内容.如果您有多个设备,则需要在此处放置一些内容,以便轻松识别当时使用的设备.可以在Xcode Organizer中找到设备ID.切换回Xcode并选择"窗口"菜单,然后选择"组织器".当管理器打开时,您会在左侧看到您的iOS设备.选择它.您可能会看到一个标记为"使用此设备进行开发"的按钮.如果您这样做,则单击它.当您的设备信息显示在屏幕顶部时,查找标记为"标识符"的值并将其复制到剪贴板.返回到Development Provisioning Assistant,并将其粘贴在Device ID中,然后单击Continue.您会看到确认已创建证书的确认.进入下一个屏幕时,需要为刚创建的配置文件分配名称.单击两次"继续"后,您将能够下载您的配置证书.下载证书后,返回Xcode Organizer并单击供应区域中的" +“按钮.在打开的窗口中,导航到您的配置文件并选择它.完成此操作后,将配置您的设备.您现在应该可以在设备上运行项目.时不时地,当我完成将预配置证书部署到设备的过程时,它不起作用.发生这种情况时,我必须先断开连接,然后重新连接我的iDevice一次或两次,然后它才能工作.
当心,置备配置文件即将到期!
复制到您设备上的配置文件只能使用大约三个月.过期后,设备上与配置文件关联的所有可执行文件将停止运行.如果要查看设备的配置文件何时过期,可以在"设置"菜单中查看它.导航到"设置”->“常规”->“配置文件"以查看您当前的规定.
字符串解析,格式设置和其他文本处理
字符串的基类是NSString类. NSString类是不可变的,因此它包含的字符串不会改变.还有一个名为NSMutableString的子类,其内容可以更改. NSString类存储Unicode字符.字符串的长度可以通过"长度"方法进行探测,而字符串中特定位置的字符可以通过” characterAtIndex"方法进行探测. 有几种初始化字符串的方法.一种方法(您现在已经看到)是将文字文本括在引号中,并在其前面加上符号(例如,@@" Hello World!".).如果您需要从其他数据(例如整数值)创建字符串,则可以使用类方法stringWithForm.该方法采用的格式字符串与您在C/C ++中可能使用的格式字符串相似.以下代码导致字符串"一打中有12个项目":
NSString* myString = [NSString withStringFormat:@"There are %d items in a dozen.", 12];
如果您将数字值表示为字符串,则可以使用xxxValue方法之一轻松地将其转换回数字值(其中xxx可以是int,int,eger,double,bool. ," float"或" long").
还有许多其他的字符串操作方法,在这里我不能全部介绍.但我鼓励您浏览NSString
文档,以详细了解可用的字符串操作方法.我将使用与本文档相关的更多可用方法.
数据持久性
在iOS设备上保留数据的方法有多种.如果您想使用传统的C语言IO,则可以使用fopen
及其相关功能来操作文件.我不会在这里介绍.相反,我将介绍一些特定于iOS的解决方案.
获取应用程序的存储路径
在iOS设备上,您的应用程序已沙箱化.您的应用程序只能写入几个文件夹.有一个/Documents文件夹,其中包含您需要保留并定期备份的信息.有一个/Cache文件夹,您可以在其中放置在两次启动之间应该可用但不能由iTunes备份的信息.还有一个/tmp文件夹,用于仅在应用程序运行时需要的信息.它无法读取或写入设备上的任何其他位置.您可以使用函数NSSearchPathForDirectoriesInDomain()获取到应用程序/Documents文件夹的路径.此功能适用于iOS和Mac OSX.您会发现它无法与所有可用选项一起使用.原因是iOS上不支持该选项,或者您的应用程序无权使用某些选项. 此函数返回满足给定条件的目录数组.我们在这里的最初使用是检索单个目录,因此当我们检索到Documents文件夹的路径时,将得到一个包含一项的数组.我们必须传递给函数的第一个参数是常量NSDocumentDirectory.顾名思义,这告诉函数我们需要Documents文件夹.第二个参数是常量NSUserDomainMask. 这表明我们想将搜索限制在应用程序文件夹内.
NSArray *myPathList =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *myPath = [myPathList objectAtIndex:0];
为了阅读或编写文件而构造文件名,我们只需要将文件名连接到我们的路径即可. NSString有一个专门用于此目的的方法.
NSString *myFilename = [myPath stringByAppendingPathComponent:@"fileName.txt"];
对于临时文件,有一个不带任何参数并返回可用于临时文件的路径的函数.它的名字是NSTemporaryDirectory();. 构造好文件名后,您可以将其用于数据持久性API之一.
清单
PLists(或属性列表)易于使用.如果仅使用一组特定的对象创建内存中的数据层次结构,则可以立即将该层次结构写入文件或从文件中加载.作为开发人员,您不必担心序列化数据.为您完成.使用该解决方案的一个缺点是,即使对数据进行了很小的更改,也必须重写整个文件.您无法进行增量更改.以下是为PList建立对象层次结构时可以使用的类:
- NSArray
- NSData
- NSDate
- NSDictionary
- 可变数组
- NSString
- NSMutableString
- NS编号 建立对象层次结构后,可以使用writeToFile方法将结构及其数据保存到文件中.它需要一个文件名和一个Atomically参数的值.如果" Atomically"设置为" yes",则数据将被直接存储到一个临时文件中,然后移动到目标文件名中,而不是直接保存到目标文件中.它花费的时间更长,但是更安全.如果设备崩溃,则最终文件状态受损的可能性较小.要加载文件的内容,我们可以实例化根元素并使用初始化方法" initWithContentsOfFile".此方法将文件路径作为唯一参数,并在填充数据时负责构建整个层次结构. 在下面的代码示例中,我使用了PList将数据保存到文件中.我用三个文本框(用于数据)和两个按钮来调用一个简单的界面,以调用保存和加载功能. 虽然不可见,但按钮下方还有一个标签控件.加载或保存数据后,将在此处显示文件的路径.单击"保存"按钮后,文本框的内容将被复制到" NSArray"中,并通过" writeToFile"方法保存到文件中.单击加载按钮后,将创建一个新的NSArray并使用initWithContentsOfFile方法进行初始化.
如果要查看文件是否确实在保存,请运行该程序,保存一些文本,然后关闭该程序.如果再次运行它,它将能够重新加载数据.
#import <uikit>
@interface DataPersistenceExampleViewController : UIViewController {
IBOutlet UITextField* textBox00;
IBOutlet UITextField* textBox01;
IBOutlet UITextField* textBox02;
IBOutlet UILabel* storageLocationLabel;
NSString* storagePath;
}
@property (retain) IBOutlet UITextField *textBox00;
@property (retain) IBOutlet UITextField *textBox01;
@property (retain) IBOutlet UITextField *textBox02;
@property (retain) IBOutlet UILabel *storageLocationLabel;
@property (retain) NSString *storagePath;
- (IBAction)saveButtonClicked:(id)sender;
- (IBAction)loadButtonClicked:(id)sender;
@end
#import "DataPersistenceExampleViewController.h"
@implementation DataPersistenceExampleViewController
@synthesize textBox00;
@synthesize textBox01;
@synthesize textBox02;
@synthesize storageLocationLabel;
//@synthesize storagePath;
- (IBAction)saveButtonClicked:(id)sender
{
NSArray* valueList = [NSArray arrayWithObjects:
textBox00.text,textBox01.text, textBox02.text, nil];
NSArray *myPathList = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *myPath = [myPathList objectAtIndex:0];
storagePath = [myPath stringByAppendingPathComponent:@"myData.txt"];
storageLocationLabel.text = storagePath;
[valueList writeToFile:storagePath atomically:true];
}
- (IBAction)loadButtonClicked:(id)sender
{
NSArray *myPathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *myPath = [myPathList objectAtIndex:0];
storagePath = [myPath stringByAppendingPathComponent:@"myData.txt"];
NSArray* valueList = [[NSArray alloc] initWithContentsOfFile:storagePath];
textBox00.text = [valueList objectAtIndex:0];
textBox01.text = [valueList objectAtIndex:1];
textBox02.text = [valueList objectAtIndex:2];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
self.textBox00=nil;
self.textBox01=nil;
self.textBox02=nil;
self.storageLocationLabel=nil;
}
- (void)dealloc {
[textBox00 release];
[textBox01 release];
[textBox02 release];
[storageLocationLabel release]
[super dealloc];
}
@end
用存档器写文件
尽管PLists易于操作,但是可以存储在PLists中的数据类型是相当基本的.存档者可以将任何类型的对象写入文件,只要该对象符合NSCoding协议即可. NSCoding定义用于读取和写入对象状态的消息.当一个对象被保存到文件中时,它的encodeWithCoder:
方法被调用,以便它可以表达其状态信息.在取消归档过程中(从文件中读取对象时),将调用initWithCoder:
方法,并提供先前由存档器保存的信息,以便对象可以重新初始化自身.
两条消息均接收类型为" NSCoder"的参数. " NSCoder"对象具有多种方法来编码不同的数据类型,例如布尔值,字节,字符串和其他类型的对象.对于每种编码方法,还有一种解码方法.
使用加速度计
设备内的加速度计可检测加速度.地球上的重力等于物体以9.81米/秒^ 2的速度加速度时所承受的力.因此,不会移动的设备将检测到重力对其施加拉力的方向.如果设备处于自由落体状态,则其加速度计的读数将接近于零(请牢记,请不要通过摔落设备进行测试).加速度计的读数由沿X,Y和Z轴的测量值组成,因此iPhone中的加速度计可以检测任何方向的运动.
每个应用程序都可以访问一个" UIAccelerometer"实例来读取加速度计数据.您没有直接实例化此实例.相反,您可以通过UIAccelerometer类中的类方法shareddcelcelometer来检索它.一旦有了此类的实例,就可以提供一个将接收数据和更新间隔的委托.最小的更新间隔是10毫秒,这将导致每秒100次更新.大多数应用程序通常不需要更新.当您不再需要加速度计数据时,可以将委托设置为" nil"以停止接收更新.当您实际上不需要加速度计数据时,不应使更新机制处于活动状态.这样做可能有助于更快地消耗电池电量.
您实现的委托方法名为" accelerometer:didAccelerate:".调用此方法时,它将同时收到对加速度计的参考和加速度读数.
根据Apple文档,以下是建议的更新频段:
如果您只需要了解设备的一般方向,而不需要加速度计数据,则应该从UIDevice类中检索设备的方向.要检索设备的方向,必须首先调用UIDevice的beginGeneratingDevice Orientation通知.调用此方法后,您可以从Orientation属性中读取当前方向.您还可以订阅UIDeviceOrientationDidChangeNotification.无论哪种情况,如果您的应用程序不再需要定向事件,则应调用endGeneratingDeviceOrientation
Notifications来关闭加速度计并节省电量.
为了演示,我正在构建一个程序,该程序将从加速度计中读取值并将其显示在屏幕上.
#import <uikit>
@interface AccelerometerDemonstrationViewController : UIViewController {
IBOutlet UITextField* AccelX;
IBOutlet UITextField* AccelY;
IBOutlet UITextField* AccelZ;
UIAccelerometer *myAccelerometer;
}
@property (retain) IBOutlet UITextField *AccelX;
@property (retain) IBOutlet UITextField *AccelY;
@property (retain) IBOutlet UITextField *AccelZ;
-(IBAction)startAccelerometerClicked:(id)sender;
-(IBAction)stopAccelerometerClicked:(id)sender;
-(void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration;
@end
#import "AccelerometerDemonstrationViewController.h"
@implementation AccelerometerDemonstrationViewController
@synthesize AccelX;
@synthesize AccelY;
@synthesize AccelZ;
-(IBAction)startAccelerometerClicked:(id)sender {
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:0.20] ;
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
}
-(IBAction)stopAccelerometerClicked:(id)sender {
[[UIAccelerometer sharedAccelerometer] setDelegate:nil];
}
-(void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration {
[AccelX.text release];
[AccelY.text release];
[AccelZ.text release];
AccelX.text = [NSString stringWithFormat:@"%f", acceleration.x];
AccelY.text = [NSString stringWithFormat:@"%f", acceleration.y];
AccelZ.text = [NSString stringWithFormat:@"%f", acceleration.z];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
self.AccelX = nul;
self.AccelY = nul;
self.AccelZ = nul;
}
- (void)dealloc {
[AccelX dealloc];
[AccelY dealloc];
[AllocZ dealloc]
[super dealloc];
}
@end
我可能会单独讨论数学方程式和算法,这对处理加速度计很有帮助.现在,我仅提及两个在以前的程序中使用加速度计时发现有用的东西.第一个方程是获得加速度计值的大小.对于不移动的设备,此数字应接近一个(尽管该数字可能会浮动,因为加速度计的精度不是100%精确到大于一个或小于一个).对于一个快速加速的数字,它将大于一个.对于自由落体的设备,它将接近零.您可以通过获取(x * x + y * y + z * z)
的平方根来获得加速度计读数的大小.另一个值得了解的方程式是获取设备沿某个平面倾斜的方向(以弧度为单位).例如,如果我有一个设备平放在桌子上,然后决定倾斜它,则可以使用" atan2(y,x)“来计算设备倾斜的方向.
下一步是什么?
尽管这篇文章足以使您开始使用UI框架中可用的控件调色板,但在某些时候,您还需要呈现自己的图形.在您对iOS开发感到更满意之后,建议您看一下[iOS图形API]的介绍(/KB/iPhone/iOSGetStarted01.aspx).
闭幕
我希望这足以让您开始进行iOS开发.我计划同时编辑本文,并随着时间的推移添加新文章,重点关注iPhone开发的各个方面.如果您有任何反馈或建议,请随时将其留在下面或给我发送消息.
致谢
我要感谢CraigNicholson和汤姆猫帮助我指出本文的原始版本及其代码中的错误或错误.
历史
- 图形API简介
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C Mobile Dev Intermediate iPhone Objective-C 新闻 翻译