IOS开发起步
XCode、Objective-C、Cocoa说的是几样东西?
答案:三样东西。
XCode:你可以把它看成是一个开发环境,就好像Visual Studio或者Netbeans或者SharpDevelop一样的玩意。你可以将Interface Builder认为是Visual Studio中用来画界面的那部分功能单独提出来的程序。
Objective-C:这是一种语言,就好像c++是一种语言,Java是一种语言,c#是一种语言,莺歌历史也是一种语言一样。
Cocoa:是一大堆函数库,就好像MFC、.NET、Swing这类玩意,人家已经写好了一堆现成的东西,你只要知道怎么用就可以了。
有些人会比较容易混淆Objective-C和Cocoa,就好像有些人会混淆c#和.NET一样。这两个东西真的是两个不一样的东西。
Objective-C是什么?
你可以把它认为是语法稍稍有点不一样的c语言。虽然第一眼望上去你可能会认为它是火星语,和你所认知的任何一种语言都不一样。
先简单列出一点差别:
问题一:我在程序中看到大量的减号、中括号和NS**这种东西,他们是什么玩意儿?
- 减号(或者加号)
减号表示一个函数、或者方法、或者消息的开始,怎么说都行。
比如c#中,一个方法的写法可能是:
private void hello(bool ishello){
//...
}
用Objective-C写出来就是
-(void) hello:(BOOL)ishello
{
//...
}
挺好懂的吧?
不过在Objective-C里面没有public和private的概念,你可以认为全是public。
而用加号的意思就是其他函数可以直接调用这个类中的这个函数,而不用创建这个类的实例。
- 中括号
中括号可以认为是如何调用你刚才写的这个方法,通常在Objective-C里说“消息”。
比如C#里你可以这么写:
this.hello(true);
在Objective-C里,就要写成:
[self hello:YES];
- NS**
老乔当年被人挤兑出苹果,自立门户的时候做了个公司叫做NextStep,里面这一整套开发包很是让一些科学家们喜欢,而现在Mac OS用的就是NextStep这一套函数库。
这些开发NextStep的人们比较自恋地把函数库里面所有的类都用NextStep的缩写打头命名,也就是NS**了。比较常见的比如:
NSLog
NSString
NSInteger
NSURL
NSImage
问题二、#import、@interface这类玩意说的是什么?
#import
你可以把它认为是#include,一样的。但是最好用#import,记住这个就行了。
- @interface等等
在Objective-C里写一个kids类:
先写一个kids.h
文件定义这个类:
再写一个kids.m文件实现:
这个写法也不一定对,主要是看看语法就行了。
####问题三、一个方法如何传递多个参数?
一个方法可以包含多个参数,不过后面的参数都要写名字。
多个参数的写法
(方法的数据类型) 函数名: (参数1数据类型) 参数1的数值的名字 参数2的名字: (参数2数据类型) 参数2值的名字 …. ;
举个例子,一个方法的定义:
-(void) setKids: (NSString *)myOldestKidName secondKid: (NSString *) mySecondOldestKidName thirdKid: (NSString *) myThirdOldestKidName;
实现这个函数的时候:
-(void) setKids: (NSString *)myOldestKidName secondKid: (NSString *) mySecondOldestKidName thirdKid: (NSString *) myThirdOldestKidName{
大儿子 = myOldestKidName;
二儿子 = mySecondOldestKidName;
三儿子 = myThirdOldestKidName;
}
调用的时候:
Kids *myKids = [[Kids alloc] init];
[myKids setKids: @”张大力” secondKid: @”张二力” thirdKid: @”张小力”];
而如果你用java写这个方法,大致的写法可能是
public void setKids( string myOldestKidName, string mySecondOldestKidName, string myThirdOldestKidName)
{
…
}
调用的时候大概的写法可能是:
Kids myKids = new Kids();
myKids.setKids (“张大力”, “张二力”, “张小力”);
明白了吧?其实不怎么难看懂。
基本上,如果你能了解下面这段代码的转换关系,你Objective-C的语法也就懂了八成了:
[[[MyClass alloc] init:[foo bar]] autorelease];
转换成C#或者Java的语法也就是:
MyClass.alloc().init(foo.bar()).autorelease();
其他的一些东西
1、 id:
Objective-C有一种比较特殊的数据类型是id。你可以把它理解为“随便”。
在Objective-C里,一切东西都是指针形式保存,你获取到的就是这个对象在内存的位置。那么id就是你知道这个位置,但是不知道里面是啥的时候的写法。
2、 同一个数组可以保存不同的对象:
比如一个数组NSArray,这种数组里面可以保存各种不同的对象,比如这个数组里:
myArray <—-|
0: (float) 234.33f
1: @”我是个好人”
2: (NSImage *) (俺的美图)
3: @”我真的是好人”
这是一个由4个东西组成的数组,这个数组包括一个浮点数,两个字符串和一个图片。
3、BOOL,YES,NO:
你可以认为YES表示C#或者Java里的true,NO表示false。而实际上YES是1,NO是0,BOOL本身就是个char。
4、IBOutlet、IBAction是啥玩意,总能看到。
这两个东西其实在语法中没有太大的作用。如果你希望在Interface Builder中能看到这个控件对象,那么在定义的时候前面加上IBOutlet,在IB里就能看到这个对象的outlet,如果你希望在Interface Builder里控制某个对象执行某些动作,就在方法前面加上(IBAction)。
而这两个东西实际上和void是一样的。
5、nil。
Objective-C里的NULL(空)就这么写,表示空指针。
6、为什么是@”字符串”而不是”字符串”
”字符串”是C的字符串,@””是把C的字符串转成NSString的一个简写. 在需要NSString的地方才需要这个转化,例如NSLog里面. 在需要C string的地方,还是用”字符串”的.
另外,@””这个转换是不支持中文的.例如NSLog(@”字符串”); 是一定输出不了中文的.
Objective-C语法
1.Object-c使用.m后缀作为代码文件的扩展名,当然你也可以使用.c和.cpp后缀名,但它们会分别调用C compiler和C++ compiler。Xcode作为iphone开发的集成环境,使用GCC作为编译器。
2.Object-c使用#import代替#include来导入声明文件。并且编译器保证#import不会被重复导入。
3.尽管Object-c也支持printf()进行控制台打印文本信息,但是更推荐使用NSLog(),并且它会自动在文本后面添加’/n’。
4.Object-c曾经也叫做NextStep,为了更好的体现其OOP的特性,NextStep里所有的对象都继承自NSObject,这点和Java的单根继承很类似。所以其Applicatio Kit里的类库前缀基本都是NS。而其Cocoa Foundation里的类库前缀基本都为NSCF。
5.Object-c提供了BOOL类型,但这个BOOL类型和C++里的并不一样,在C++里一切非0值的东西都为true,而为0值的为false。但是Object-c里1为true并被宏定义为YES,0为false并被宏定义为NO。
所以,如果读者写下面的代码,肯定是错误的:
因为areIntsDifferent_faulty方法返回的是两个整数的差,如果这个差不为1,那么永远不会为YES。
6.Object-c里仍然继承了C语言的传统,其primitive type只有int、char、float、BOOL并用它们表达一切。
7.Object-c采用@interface来进行接口的声明,并采用@implementation对声明进行实现。
8.Object-c在Foundation Kit里提供了很多基础类库,常用的如NSString、NSRange、NSLog、NSPoint、NSRect等,这点和Symbian里提供的绘制接口很类似。
9.Object-c也支持Collection类库,并充分借鉴了Java Collection的设计,比如NSArray、NSMutableArray等。这些类库提供基本的插入、删除、排序操作,需要注意的是NSMutableArray才提供上述操作,而NSArray为不可修改的,这有点类似于Java的String和StringBuffer,而iphone里也提供了NSString和NSMutableString!
10.Object-c里采用跌代器的概念进行元素的遍历,NSEnumerator的典型用法如下:
Objective-C的OO特性
1.继承
典型的继承用法如下:
上述两个类Circle、Rectangle的属性和行为非常类似,所以我们可以通过封装、继承的方法进行代码构造。如下:
下面分别利用继承实现Circle和Rectangle,如下:
2.多态
Object-c利用[super Init]之类的语法调用父类的方法,每个类都有一个指向自己的self指针,这点和C++的隐含this指针是类似的。
Object-c并不支持多继承,因为多继承的开销过大,需要解决虚基类的问题。
3.内存管理
Object-c在内存管理上也采用大多数编译器所采用的reference counting技术,亦即编译器为每个对象维护一个引用计数,在这个引用计数为0时释放对象所占用的资源。
当Java遇见了Objective-C
我们来看这个社交网络应用的例子,这个应用需要建立一个联系册,让你和朋友们时常保持联系。朋友的档案存储在FriendProfile对象中,包含四个字段:朋友的名字、城市、国家和电话号码,如Listing One所示:
在这个例子中,接口FriendProfile:NSObject
表示我们定义了一个叫做FriendProfile的接口,它从NSObject基类中继承各种功能。NSObject是Objective-C的根类,大多数Objective-C中用到的类都会从中继承,这和Java中的Object类相似。接下来,我们分配多个NSString类型变量(等同于Java中的String类型)用来存储朋友的数据。
然后是建立FriendProfile类,使用@synthesize
关键字自动创建各种get和set方法。
建立一个FriendProfile对象可以使用如下的语句:
FriendProfile * profile = [[FriendProfile alloc] init];
这里的alloc和init就像Java里的new关键字,用来在内存中建立FriendProfile对象。接下来,就可以给对象的各种字段赋值了。
或者可以更简单一点:
在Java中,如果我们想写一个FriendProfile类,所做的和Objective-C会非常相像,就像Listing Two所示:
Listing Two中提供了相似的字段,但是那些get和set必须清楚的写出来。现在我们看看怎样在通讯录里添加一个新朋友,参加Listing Three:
在这个例子里,FriendlyServletController类从HTTPServlet中获取行为,HTTPServlet是Java的客户端组件类,负责处理浏览器的请求。当用户登入网站并且决定添加一个朋友时,他会在HTML表单的字段中填入数据,表单提交时,Servlet收到并验证请求的参数,并创建一个FriendProfile对象,在内存中存储数据。而ProfileManager类会把你的FriendProfile对象存储到数据库中。
Objective-C的MVC模式
UIViewController实际上是一个控制器组件,用来触发业务逻辑,更新客户端的视图。
如果你想在Xcode中创建一个UIViewController类型的对象,可以选择通过XIB文件来创建。这种特殊的Xcode文件定义了图形用户界面或者说视图,包含了各种不同的控件,比如按钮、图表和标签等等。
回到我们的例子中来,假设你已经在联系列表中添加了几个朋友,现在想按下某个朋友的链接来看查看他的详细信息,这个功能可以通过定义控制器类来完成。代码请见Listing Four:
这段代码中,我们创建了一个FriendProfileViewController实例,在我们定义的View Bundle中进行初始化,显示出朋友的各种信息。
模型在装载视图时开始启动。每个控制器都有一些从父类UIViewController继承而来的生命周期方法。比如ViewdidLoad方法就是其中之一,它负责在视图装载之后的额外设置,从数据库中取出信息,更新视图。在最简单的情况下,我们的视图包含一系列标签,或者是UILabel类型的对象,可以在应用运行时设置各种文本,用户可以立即看见朋友信息被更新了。
Objective-C的数据库访问
苹果推荐开发者使用称为Core Data的Cocoa API框架进行数据库存取操作。Core Data框架能够直接与SQLite数据库相结合(我们例子中的数据库运行在移动设备上)。Core Data隐藏了复杂的SQL操作,取而代之的是非常方便的NSManagedObject界面,你可以直接操作整个对象实例的各种字段,这些字段可以自动存入数据库。Core Data框架的另一个方便之处是在数据库中创建表(以及向表中添加关联与限制),这些都可以在Core Data的用户界面中完成。
现在回到我们的社交网络应用例子,看看怎么从数据库中取出朋友的信息。我们使用SQLite 和Core Data API,但首先我们要稍微修改一下FriendProfile类,代码请见Listing Six:
这里的FriendProfile类与Listing One中的不同之处在于在这里我加入了Core Data框架的头文件。而且在这里我们的类是从NSManagedObject中扩展出来,带有了Core Data对象需要的全部基本行为。Core Data的NSManagedObject类中使用到的Accessor则在应用运行时动态创建。如果你想在FriendProfile类中声明或使用属性,但不想在编译时出现缺少方法的警告,可以使用@dynamic指令,而不是@synthesize指令。
使用NSManagedObject API有些复杂,但你理解之后就会变得很好用。Listing Seven是一个示例方法,从数据库的FRIENDPROFILE表中取得朋友的信息。表包含四列:NAME、COUNTRY、CITY和PHONENBR。
getFriendProfileObjectbyName方法把朋友的姓名作为一个参数接收过来。通过使用Core Data API,我们可以指定在哪一个表中进行查询和排序,并且在后台执行SQL语句。
SQL>select * from FriendProfile where name = "Albert";
Core Data API有许多种没有封装的“半成品”代码,可以访问NSManagedObjectContext、NSPersistentStoreCoordinator和NSManagedObjectModel对象。你可以复制这些代码,只要你取得了FriendProfile对象,就能以下面的形式取得它的属性:
总的来说,Core Data是一个非常有用的功能,可以让你通过图表来定义数据表和管理,可以动态生成相应的对象,而且无需使用复杂的SQL语句。但不好的方面是这里有大量的没有经过封装的代码,这样你在使用它们与测试时需要非常小心。
结论
除去语法结构与运行平台的不同,使用Objective-C进行iPhone开发与使用Java进行网络应用开发在下面几个方面是相同的:
◆两种语言都是面向对象的
◆两种语言使用同样的设计模式,例如MVC
◆两种语言使用相似的数据库存储技术,例如ORM
然而,对于Java开发者,使用Objective-C时在有些地方要格外小心:
◆创建对象:Java对象是在运行时通过new关键字创建的。因此Java程序员无需担心内存分配问题。而在Objective-C中,一个对象可以由三个关键字创建,alloc、new或者copy,这三个关键字在创建对象时都会增加对象的持有计数(retain count),持有计数是Objective-C特有的内存管理方法,显示有多少个指针指向对象,是否可以被内存管理器回收。
◆销毁对象:由于强大的垃圾回收机制,Java的内存管理工作极度简单。Java的引用对象都存储在JVM的堆内存中,一旦不再被引用,就可以作为垃圾回收。Objective-C使用的是内存管理器,而不是垃圾回收器。如果你使用上面说的三种方法在内存中创建了一个对象,那么必须使用release方法来释放对象。release方法会减少持有计数,当计数降到0时,被引用的对象会接受一个来自高级类的dealloc方法,释放它占用的内存并重新分配。如果忘记了释放内存或释放失败,那么会造成内存泄露和不可预见的错误。
◆过多释放和过早重新分配内存:由于垃圾回收机制,Java程序员可以完全不考虑这些问题。但Objective-C程序员需要小心,不能释放出比分配的更多的内存。如果在已经重新分配的对象上过多释放内存,就会造成应用的崩溃。
上面这些例子说明了Objective-C和Java在语法和语言元素上有很多相同之处。更重要的是,它们解决问题的思路和用到的组件也是非常相似的。如果你是Java程序员,相信你在看完这篇文章后,转向Objective-C的道路会更加通顺。
-EOF-