iPhone开发入门(1)—-程序员眼中的iPhone

 

http://blog.csdn.net/itudou_2010/article/details/5492272

iPhone开发入门(1)—-程序员眼中的iPhone

  • 博主:易飞扬
  • 原文链接 : http://www.yifeiyang.net/iphone-development-introduction-1-programmers-in-the-eyes-of-the-iphone/
  • 转载请保留上面文字。

    自去年 iPhone 面世以来,开创了移动设备内容服务的一种新的模式—-程序商店(App Store)。它极大地降低了移动设备应用程序开发的成本,即使普通人也能进入这个市场。就像在PC上开发应用程序一样,我们的投入只是一台电脑,一个手机。

    截止到目前为止,许多大的电话设备提供商/运营商和软件企业都加入了这个新的模式,Nokia的 Ovi Store (2009年5月26日开始),微软的 Windows Marketplace for Mobile ,Google的 Android Market (2008年10月22日开始)等都已经或者准备进入这个市场了。

    就像以前开发PC上的共享软件一样,许多程序员和小的企业加入了开发队伍的行列。只要有新意捞到你人生的第一桶金不是什么问题。国外的一个22岁开发者只用了20分钟开发了一款叫做Sound Grenade的程序,一个小时的收入就是200美金!!!一个月70,80万人民币轻轻松松啊。要论这个程序,没有什么技术可言,纯粹一个垃圾,可是就是这么一个垃圾程序,却受到很多人的欢迎,这只能说作者有创意了。顺便说一说,程序本身是免费的,主要是靠流量带来的广告收入(和网页广告一个道理)。

    从今天开始,介绍一下 iPhone 应用程序的开发,发布流程,让我们也把自己的创意写成程序,展现一下实力吧。

    iPhone开发入门(1)—-程序员眼中的iPhone

    iPhone 不单纯是具有电话功能的 iPod,它的有许多有魅力的地方。作为一个应用程序的开发者,我们首先来看看它的这些特征。

    首先,手机上的按键没有了,应用程序的按键,开关与电话的按键统一了起来。取而代之的是触摸屏技术,应用程序的开发者从此不再受按键的限制,可以自由的设计UI的风格。喜欢把按键放哪就放哪,即使不用按键,类似于PC应用程序的滚动条/拖动条等也可以在触摸屏上实现。

    iPhone 中已经提供了从按键到滚动条等一系列的UI控件。当然,你也可以不使用这些控件,而是自己自作独特的UI控件。

    制作iPhone应用程序需要的东西

    首先是开发环境,标注的配置是以下的环境:

    • Mac 电脑(CPU要是Intel的)
    • Mac OS X v10.5(Leopard)或以上的版本
    • 一台 iPhone 或者 iPod touch

    当然你也可以在windows或者linux上安装开发环境。iphonedevonlinux有详细的介绍,使用toolchain在Cygwin或者linux下进行iPhone的开发,如果你想省些钞票或者想更深入的了解 iPhone OS 那么不妨试试它。这一部分,我在以后的章节中会专门介绍。

    另外,开发所必要的软件,SDK,IDE等可以免费从Apple的网上下载。如果你想开发有GPS和照相机或者重力传感器功能的程序,那么需要iPhone 3G,其他一般的程序iPod touch就可以了。另外,如果想要把做成的程序发布出去,还需要到Apple Developer Connection花99美金登录。

    Cocoa Touch与Xcode

    Cocoa Touch

    刚才介绍了开发iPhone应用程序的时候,可以选择许多现成的UI控件。实际使用的过程当中,使用叫做「Cocoa Touch」的程序开发组件库,它类似于windows下开发时所用的MFC,.NET FrameWork。利用 Cocoa Touch,开发者不用考虑设备的特性和画面特性,就可以简单的构筑GUI。

    另外,利用Cocoa Touch,可以开发具有以下iPhone功能的应用程序。

    照相功能

    利用照相机拍照,并将照片保存到iPhone。

    当前位置取得功能

    要用iPhone取得当前位置,可以使用 GPS,无线LAN,基站等3种方式。应用程序不用考虑使用哪种方式,系统通过最佳的方式取得现在的位置信息。

    Xcode

    Xcode是Mac OS X下的IDE开发环境,也是免费的。主要包含有GUI设计用的「Interface Builder」,iPhone应用程序模拟器,编译器等。

    Objective-C语言

    iPhone开发的时候,基本使用Objective-C语言。它是扩充C的面向对象编程语言,也是创建Mac OS X应用程序的首选语言。如果你会C或者C++,理解Objective-C应该很快。并且因为Objective-C可以在gcc运作的系统写和编译,你也可以混合Objective-C和C/C++来写程序,或者使用原先的C/C++库。

    并且使用Cocoa Touch来开发程序,自己写的代码量也会相应减少,应为大部分的算法,逻辑部分都被程序库吸收了。

    现在就开始开发iPhone应用程序

    准备好了所有的工具,我们就可以开始开发iPhone应用程序了。当然学习Cocoa Touch,Objective-C语言也是很重要的,这些我以后会有专门介绍。

    在开发自己的应用程序之前,最重要的是什么呢? 是开发程序的知识和技能吗?不是的,最重要的是开发者的激情和创意。有了激情,我们能产生好的创意,有了好的创意,我们才能写出优秀的应用程序。

    iPhone开发入门(2)—从iPhone SDK开始

    这一回,我们从实际开发所需要的SDK开始,讲解一些基本的设定。

    什么是iPhone SDK

    上一回我们简单地说明了iPhone的应用程序开发环境Xcode。Xcode本来是Mac上的应用程序开发工具,这里只是导入了开发iPhone应用程序所需的SDK而已。正是因为安装了 iPhone SDK,我们才能通过Xcode来使用Cocoa Touch,在开发中使用MAC下的「iPhone 模拟器」。

    如果你的MAC上没有事先安装Xcode也不要紧,在安装iPhone SDK的同时,Xcode会被一起安装。

    下载iPhone SDK

    iPhone SDK可由Apple Developer Connection免费下载。下载之前需要注册「Apple ID」。如果你之前使用 iTunes 下载音乐,或者在线使用 Apple Store 购买商品,那么你使用之前注册的「Apple ID」就可以了。

    Apple Develper Connection

     

    Apple Develper Connection

    iPhone SDK

     

    下载iPhone SDK

    注册Apple ID

    Create Apple ID

     

    如果你已经有Apple ID的话。点击「Log in」,否则的话点击「Create Apple ID」

    register info

     

    注册必要的信息,最好填写英文哦

    登录iPhone Dev Center

    用刚做好的Apple ID可以登录iPhone Dev Center

    register info

     

    登录好了,就可以下载iPhone SDK了。

    安装iPhone SDK

    下载好了,就开始安装吧。打开下载的文件,双击里面的「iPhone SDK.mkpg」文件。

    install iPhone SDK

     

    安装iPhone SDK

    接下来只需一路确定,下一步,就可以完成了。接下来启动Xcode。

    使用Xcode制作程序

    点击硬盘的Developer→Applications→Xcode.app就可以启动Xcode了。

    install iPhone SDK

     

    Xcode的位置

    创建工程

    由Xcode的菜单中选择「文件」→「创建新项目...」。然后在模板中选择「View-Based Application」。

    Create iPhone Application

     

    确认左边被选地是「iPhone OS」→「Application」。

    Xcode(3.1.2)

     

    Xcode(3.1.2)

    在上面的窗口中可以阅览文件,编辑文件,或者启动「Interface Builder」。

    编译与执行

    接下来,可以点击编译,成功后会自动打开iPhone程序模拟器「Simulator ~」。如下图:

    iPhone Simulator

     

    iPhone Simulator

 

iPhone开发入门(4)---应用程序开发的第一步

这一回开始,介绍一下实际的iPhone应用程序开发的步骤。

创建Xcode工程

现在就让我们开始第一个iPhone应用程序吧。启动Xcode,在Xcode的菜单中点击「文件」→「新项目...」。

View-Based Application

 

从模板中选择「View-Based Application」

模板选择「View-Based Application」。这回取得开发例子是已经登录到App Store的一款应用程序「BargainSale」,它是自动计算打折后价格的一款应用程序。取名为「BargainSale」后,选择保存。

保存之后,会基于选择的模板生成工程与源代码文件。我们来看看都有哪些。

源代码文件

View-Based Application

 

左边的Classes目录中保存了自动生成的.h和.m文件。他们是Objective-C语言的头文件和源代码文件。

XIB文件

XIB文件是记录应用程序画面构成和配置的文件。位于Resources目录下。用Cocoa Touch来开发程序,编辑XIB文件是最基本的。编辑XIB文件的时候,使用「Interface Builder」可以在画面上简单,随意的拖动各种控件。

XIB文件与Interface Builder

 

XIB文件与Interface Builder

资源文件

其他为了制作应用程序的文件都是资源文件。XIB文件也是资源文件的一种,因为比较重要,就单独罗列出来了。比较简单的应用程序也许不需要什么资源文件,但是随着程序的复杂度提升,做好资源文件的管理也很重要。

Interface Builder

前面已经说到了用Xcode附带的「Interface Builder」来编辑XIB文件。在Xcode中双击XIB文件,就会自动启动Interface Builder。

Interface Builder启动之后,点击Tools→Library可以打开标准容器库帮助。这里罗列了 Cocoa Touch 开发时用到的各种组件。通过 Library→Cocoa Touch Plugin→Inputs & Values,可以更加深入的了解各种组件的使用。

Interface Builder

 

由菜单选择程序库

比如,表示文字的Label控件,Round Rect Button按钮,Text Field 文字框。你可以选择喜欢的控件拖动到你的配置中。

InterfaceBuilder & Library

 

配置控件

标签

首先我们来使用表示文字用的「Label」控件。

Label

 

Label控件

起初控件缺省的显示Label几个字母,这里我们双击它,将其改为「123456789」。

Label

 

Text Field

接下来,我们将「Text Field」控件拖动到View视图上。

Text Field

 

Text Field

这里的Text Field在实际iPhone使用的时候,单击它就会弹出键盘。标准的键盘是输入文字的,这里因为只是输入价格,所以限定为数字键盘。

Text Field

 

如果你想更改Text Field的缺省设定,点击Interface Builder菜单栏的Tools→Inspector。

Inspector

 

Inspector

在Inspector内,你可以更改各种控件的属性。

Inspector

 

Inspector的上部有4个并排的tab按钮。设定控件属性的时候,选择最左边的tab。这里Text Field只需要输入数字,所以将键盘种类「Keyboard Type」选择为「Number Pad」就好了。

Inspector

 

KeyBoard Type

Segmented Control

Segmented Control是一个多选一的按钮。这里,我们使用它来表示各种折扣的比率。

Segmented Control

 

Segmented Control

配置好了,就可以设定不同的选择。首先设置4种折扣率。在Inspector的「Segments」中选择4。

Segmented Control

 

接下来,设定不同折扣的比率。在Segmented Control控件的不同按钮上双击,设定折扣文字。

Segmented Control

 

编译与测试

到这里为止,我们已经配置了最基本的控件。也许你会留意到这些控件都被放到屏幕的上半部了,当你运行程序的时候你就会明白其中的缘由了。先编译一下看看。在Xcode中点击「编译」,并选择保存当前工程后,就可以开始编译了。

编译完了之后,启动iPhone模拟器,就可以看到我们刚刚配置好的程序界面了。注意在iPhone模拟器上,是由鼠标代替实际设备上的触摸动作的。

单击Segmented Control控件,选择20%后来确认一下。或者点击Text Field控件,键盘被表示出来了吧。

Applcation

 

就像刚才在Segmented Control中设定的那样,只有数字键盘表示出来了。这个键盘占据了屏幕下方的领域。这也正是我们刚才将所有的控件都配置到屏幕上方的原因。

如果你将控件配置到了屏幕的下半部分时又使用了键盘,那么这些控件会被键盘遮盖掉。所以当有使用键盘的时候,建议将控件都设置到屏幕上方,否则需要程序动态的更改控件位置,比较麻烦。


 

这回我们主要学习了控件的配置与使用,下一回我们将具体实现输入价格到打折后价格的计算与表示。如下图:

Applcation

 

 

iPhone开发入门(5)---iPhone编程初步

上一回 我们学习了用Interface Builder来制作程序的界面。这回我们来学习程序中具体的操作和处理。

确认操作与处理

首先让我们确认一下用户怎样操作控件,程序应该怎样处理这些操作。

这个应用程序是通过用户输入商品价格,并选择折扣比率,来计算打折后的价格。用户需要以下两步操作 :

  • 输入商品金额
  • 选择折扣率

这之后,程序中计算打折后的金额并表示输出。

Outlet与Action

在我们开始编程之前,需要记住以下两个单词。在以后我们会经常用到它们。

Outlet

通过outlet,我们可以从控件中取出信息,或者将新的信息赋予控件。按照词典上的解释outlet可以理解为插座的意思。界面上配置的每个控件,就像是通过“插座”与界面连接。这里,将程序设置在“插座”内,实现控件与界面间的信息交换。

Action

Action就是指程序中具体的行为,处理。应用程序将按照Action内实现的内容来处理。比如,「按下按钮后的处理」或者「输入文字后的处理」等等。

编写程序

接下来我们就开始写程序了。最初的程序在Interface Builder上编写。

生成Outlet

刚才已经说到,这个程序中需要编程的控件分别是用户输入商品金额与选择折扣率,以及在画面上显示打折后的金额。就是说,我们需要生成3个Outlet。

File's Owner

 

File's Owner中表示程序的ICON

在Interface Builder内部,程序的实体用ICON来表示。其中「File's Owner」的ICON就是表示程序实体。在File's Owner中可以生成Outlet。

选择File's Owner,点击Inspector的最右边的Identity标签。其中「Class Outlets」项目就是Outlet的表格。点击「+」可以追加新的Outlet。

add Outlet

 

追加Outlet

首先我们创建一个名为「motone」的Outlet,代表商品的原价。第二个为「waribiki」,代表选择的折扣率。最后创建一个名为「kekka」的Outlet,代表打折后的价格,用来放入界面中的控件。像是下图所示:

outlet

 

生成Action

与Outlet一样,ACtion也是由Inspector做成。在刚才的 Class Outlets 之上有「Class Actions」。这里我们只需要处理「表示打折以后的价格」,在Class Actions内追加一个Action就可以了。缺省命名为「myAction1: 」。

action

 

追加Action

与控件关联

接下来,我们将做好的Outlet与控件关联起来。在“File's Owner”ICON上点右键,在出现的窗口上可以看到刚才生成的Outlet和Action。如下图:

Outlet

 

每个Outlet的右边都有一个圆圈的选择按钮(鼠标光标移动到其上面后,会变成一个加号),将其拖拽到对应界面上的控件。这之后,从圆圈到控件会有一根线连接起来,控件也会被蓝色的线条框包住。如下图:

Outlet

 

关联之后,Outlet的名称的右侧将表示控件的名称。同样的方法将3个控件都与对应的Outlet关联。如果想要取消关联,点击表示部分的×后就可以了。

Action的接续方法与Outlet一样,也是通过拖拽来实现。这里我们就将「myAction1: 」右边的圆圈按钮与对应的Segmented控件关联。如下图:

Outlet

 

关联之后,会让你选择具体的行为事件,这里因为是更改折扣比率,所以选择「Value Changed」。

action

 

创建文件

截止到现在,我们还没有写过一行代码。其实,在你将Outlet与控件关联的时候,Interface Builder已经代你写了不少程序了。先让我们来确认一下:

选择File's Owner,在Interface Builder菜单上点击[File]→[Write Class Files...]保存文件到(BargainSaleViewController.m)。

action

 

如果已经有文件存在了,就覆盖它。这样,源代码文件就被保存起来了。我们打开来看看。

action

 

Outlet被定义在.h头文件中(BargainSaleViewController.h)。你可以打开它确认一下。「motone」「waribiki」「kekka」3个Outlet,Action「myAction1: 」。

编写Action

接下来我们来实现具体的Action。打开有「myAction1:」的.m文件(BargainSaleViewController.m)。在「myAction1:」函数内用下面的代码来实现。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
- (IBAction)myAction1:(id)sender { int kakaku = [[motone text] intValue]; switch([waribiki selectedSegmentIndex]){ case 0: kakaku = kakaku * (1.f - 0.2f); break; case 1: kakaku = kakaku * (1.f - 0.3f); break; case 2: kakaku = kakaku * (1.f - 0.4f); break; case 3: kakaku = kakaku * (1.f - 0.5f); break; default: break; } [kekka setText:[NSString stringWithFormat:@"%d", kakaku]]; }

之后编译,就可以启动模拟器来测试了。

action

 

 

iPhone开发入门(6)--- Action与Objective-C

我们先来分析一下上一回初次接触的Objective-C代码。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
- (IBAction)myAction1:(id)sender { int kakaku = [[motone text] intValue]; switch([waribiki selectedSegmentIndex]){ case 0: kakaku = kakaku * (1.f - 0.2f); break; case 1: kakaku = kakaku * (1.f - 0.3f); break; case 2: kakaku = kakaku * (1.f - 0.4f); break; case 3: kakaku = kakaku * (1.f - 0.5f); break; default: break; } [kekka setText:[NSString stringWithFormat:@"%d", kakaku]]; }

这里的代码是使用Segmented Control实现折扣率变化时的Action。让我们从最初的代码开始。

取出值

Text Field控件中取得输入值

首先,我们取出用户输入的商品原价。前面已经介绍过了,控件的值都是通过Outlet来得到的。这里Text Field的Outlet是「motone」,其代码如下:

1
int kakaku = [[motone text] intValue];

等号右边就是通过Outlet得到值的方法。将其赋值到变量kakaku中。

Segmented Control控件中取得值

接下来我们从Segmented Control控件的Outlet「waribiki」中取得其设定值。这里,我们取出的是Segmented Control控件中的「第几个按钮被按下」。不同的按钮对应不同的折扣率。比如最左边的按钮是20%。

1
[waribiki selectedSegmentIndex]

这里最左边的是索引是0,最右边是3。

Objective-C的语法

接触过C语言编程的朋友也许很快能适应Objective-C的语法的语法,毕竟它是由C语言演化过来的语言(其中大量继承了Smalltalk语言的思想)。我们以上面两个控件为例,来学习一下它的基本语法。

Objective-C call

Objective-C的函数调用

函数调用首先用 [ ] 来包住实现代码。函数调用的对象叫做接收器(receiver,可以理解为对象实例)。

用Interface Builder配置的控件Outlet就是接收器。接收器与函数(Objective-C中叫做消息)之间用空格分开。用[ ]括住的一个整体就是一次函数调用。

你也可以嵌套的使用这样的函数调用形式。比如从Text Field控件中取得输入值的时候,就是先在「motone」中执行了「text」,取出具体的文字对象,然后以其为接收器来执行「intValue」消息。这样,我们最终得到的结果就是一个整形的值。

计算与表示结果

接下来我们来计算打折后的价格。由商品原价「kakaku」和由Segmented Control控件得到的打折率需要计算出商品打折后的价格。比如选择最左边20%的时候,计算公式如下:

1
kakaku = kakaku * (1.f - 0.2f);

小数后面的「f」表示「float」。如果将整数作为小数来使用的情况下,使用「1.f」而不是「1f」。

不同的折扣率需要不同的计算方法,这里使用switch语法来区分各种情形:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
switch([waribiki selectedSegmentIndex]){ case 0: kakaku = kakaku * (1.f - 0.2f); break; case 1: kakaku = kakaku * (1.f - 0.3f); break; case 2: kakaku = kakaku * (1.f - 0.4f); break; case 3: kakaku = kakaku * (1.f - 0.5f); break; default: break; }

接下来我们使用Label控件的Outlet「kekka」来表示计算的到得打折扣价格「kakaku」。

1
[kekka setText:[NSString stringWithFormat:@"%d", kakaku]];

与上面的函数调用不同,这里增加了参数。

Objective-C call

Objective-C的函数调用

「setText」函数的参数是一个字符串,而「kakaku」变量是一个整数,所以我们先将整数变换为字符串型「NSString」。这里我们使用到了「NSString」的字符串格式函数「stringWithFormat」。最后再通过Label控件的Outlet「kekka」经具体的值表示出来。

在以后的学习中,我们将更加深入地学习Objective-C的各种语法和思想。

 

iPhone开发入门(7)--- 从C/C++语言到Objective-C语言

Objective-C,通常写作ObjC和较少用的Objective C或Obj-C,是扩充C的面向对象编程语言。所以有一定C/C++语言基础理解和掌握Objective-C也会相应的快些。这回,我们将比较着学习Objective-C语言,掌握其语法并理解其思想。

语法

让我们先来看看C++和Objective-C中对于类的宣言 :

C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include "BaseClass.h" class MyClass : public BaseClass { public: MyClass(); virtual ~MyClass(); virtual int GetValue() const; virtual void SetValue(int inValue); bool IsValid() const; static MyClass* GetInstance(); private: int mValue; static MyClass* sInstance; };
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#import "BaseClass.h" @interface MyClass : BaseClass { int mValue; } - (int) getValue; - (void) setValue: (int) inValue; - (BOOL) isValid; + (MyClass*) getInstance; @end

通过比较上面两段代码,从语法的角度上我们看到 Objective-C 语言有以下特点:

  • 用 #import 取代了 #include
#import 相当于 C/C++ 语言中的 #include+#pragma once。当头文件嵌套包含的时候,它的作用就发挥出来了。 当某一头文件已经被读取后,又一次被 #include 的时候,#pragma once 这会跳过该次读取。 比如我们在C/C++语言的头文件中常常这样定义,就是为了实现 #pragma once 而做的 :
1 2 3
#ifndef INCLUDED_BASECLASS_H #include "BaseClass.h" #endif
  • 继承的时候没有限定符
继承都是 public 的。
  • 没有构建和虚构函数
  • 成员变量/函数没有限定符
成员变量缺省是 private 的,而函数是 public 的。
  • 没有const关键字
  • 没有virtual关键字
Objective-C 中函数缺省的就是 virtual 的。

接下来再看看具体的实现 :

C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include "MyClass.h" #include <assert.h> MyClass* MyClass::sInstance = 0; MyClass::MyClass() : mValue(0) { } MyClass::~MyClass() { mValue = -1; } int MyClass::GetValue() const { return (mValue); } void MyClass::SetValue(int inValue) { assert(IsValid()); mValue = inValue; } bool MyClass::IsValid() const { return (0 <= inValue && inValue <= 1000); } MyClass* MyClass::GetInstance() { if (sInstance == 0) { sInstance = new MyClass(); } return (sInstance); }
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#import "MyClass.h" static MyClass* sInstance = 0; @implementation MyClass - (int) getValue { return (mValue); } - (void) setValue: (int) inValue { NSParameterAssert([self isValid]); mValue = inValue; } - (BOOL) isValid { return (0 <= inValue && inValue <= 1000); } + (MyClass*) getInstance { if (sInstance == 0) { sInstance = [MyClass alloc]; } return (sInstance); } @end
  • 实例方法
方法前面的“-”是实例方法(类似于C++中的类成员函数)
  • 类方法
前缀为“+”的是类方法(类似于C++中的静态成员函数,或者是全局函数)
  • 类变量
与C/C++语言中的静态变量一样,Objective-C 中的类变量就是以 static 声明的变量。(只在当前定义文件中有效) 如果子类也想参照父类中的类变量的时候,须定义属性参照方法(类方法)。(这与面向对象中的封装概念有所背驰,降低了凝聚度
  • 单一继承
Objective-C 与 Java 语言一样,都是单一继承。 如果想实现多重继承,可以只用类似Java 中 implements 的方法。(Objective-C 中叫做 protocol)
  • 发送消息
Objective-C 中类似于C/C++中函数调用的地方都被称作“发送消息”。调用某个函数,被称为发送了某个消息。其形式如下图所示 :

Objective-C message

Objective-C的发送消息

  • 方法,SEL,方法实现
Objective-C 中方法,SEL型,实现的关系如如下图所示 :

Objective-C method

Objective-C的方法

概念

SEL,IMP的定义

接下来,我们来看看 Objective-C 语言中的头文件 objc.h 的定义 :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// objc.h  typedef struct objc_class *Class; typedef struct objc_object { Class isa; } *id; typedef struct objc_selector *SEL; typedef id (*IMP)(id, SEL, …); typedef signed char BOOL; #define YES (BOOL)1 #define NO (BOOL)0 #ifndef Nil #define Nil 0 /* id of Nil class */ #endif #ifndef nil #define nil 0 /* id of Nil instance */ #endif
id

id和void *并非完全一样。在上面的代码中,id是指向struct objc_object的一个指针,这个意思基本上是说,id是一个指向任何一个继承了Object(或者NSObject)类的对象。需要注意的是id是一个指针,所以你在使用id的时候不需要加星号。比如id foo=nil定义了一个nil指针,这个指针指向NSObject的一个任意子类。而id *foo=nil则定义了一个指针,这个指针指向另一个指针,被指向的这个指针指向NSObject的一个子类。

Objective-C id Object

Objective-C的Object

nil

nil和C语言的NULL相同,在objc/objc.h中定义。nil表示一个Objctive-C对象,这个对象的指针指向空(没有东西就是空)。

Nil

首字母大写的Nil和nil有一点不一样,Nil定义一个指向空的类(是Class,而不是对象)。

SEL

SEL是“selector”的一个类型,表示一个方法的名字。比如以下方法:

-[Foo count] 和 -[Bar count] 使用同一个selector,它们的selector叫做count。

在上面的头文件里我们看到,SEL是指向 struct objc_selector的指针,但是objc_selector是什么呢?那么实际上,你使用GNU Objective-C的运行时间库和NeXT Objective-C的运行运行时间库(Mac OS X使用NeXT的运行时间库)时,它们的定义是不一样的。实际上Mac OSX仅仅将SEL映射为C字符串。比如,我们定义一个Foo的类,这个类带有一个- (int) blah方法,那么以下代码:

1
NSLog (@"SEL=%s", @selector(blah));

会输出为 SEL=blah。说白了SEL就是返回方法名。

这样的机制大大的增加了我们的程序的灵活性,我们可以通过给一个方法传递SEL参数,让这个方法动态的执行某一个方法;我们也可以通过配置文件指定需要执行的方法,程序读取配置文件之后把方法的字符串翻译成为SEL变量然后给相应的对象发送这个消息。

在 Objective-C 运行时库中,selector 是作为数组来管理的。这都是从效率的角度出发:函数调用的时候,不是通过方法名字比较而是指针值的比较来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。

这样就必须保证所有类中的 selector 须指向同一实体(数组)。一旦有新的类被定义,其中的 selector 也需要映射到这个数组中。

实际情况下,总共有两种 selector 的数组:预先定义好的内置selector数组和用于动态追加的selector数组。

  • 内置selector
简单地说,内置的selector就是一个大的字符串数组。定义在objc-sel-table.h文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
#define NUM_BUILTIN_SELS 16371 /* base-2 log of greatest power of 2 < NUM_BUILTIN_SELS */ #define LG_NUM_BUILTIN_SELS 13 static const char * const _objc_builtin_selectors[NUM_BUILTIN_SELS] = { ".cxx_construct", ".cxx_destruct", "CGColorSpace", "CGCompositeOperationInContext:", "CIContext", "CI_affineTransform", "CI_arrayWithAffineTransform:", "CI_copyWithZone:map:", "CI_initWithAffineTransform:", "CI_initWithRect:", "CI_rect", "CTM", "DOMDocument", "DTD", ... };
可以看到,数组的大小NUM_BUILTIN_SELS定义为16371。字符串按照字母顺序排序,简单的都是为了运行时检索的速度(二分法查找)。 从定义好的 selector 名称我们可以看到一些新的方法名称,比如 CIConetext,CI开头的方法是由Tiger开始导入的程序库。 每次系统更新的时候,这个数组也是需要更新的。
  • 动态追加selector
另一个用于动态追加的 selector,其定义在 objc-sel.m 和 objc-sel-set.m 文件中 新的 selector 都被追加到 _buckets 成员中,其中追加和搜索使用 Hash 算法。
1 2 3 4 5 6 7 8
static struct __objc_sel_set *_objc_selectors = NULL; struct __objc_sel_set { uint32_t _count; uint32_t _capacity; uint32_t _bucketsNum; SEL *_buckets; };
IMP

从上面的头文件中我们可以看到,IMP定义为

1
id (*IMP) (id, SEL, …)。

这样说来,IMP是一个指向函数的指针,这个被指向的函数包括id(“self”指针),调用的SEL(方法名),再加上一些其他参数。说白了IMP就是实现方法。

我们取得了函数指针之后,也就意味着我们取得了执行的时候的这段方法的代码的入口,这样我们就可以像普通的C语言函数调用一样使用这个函数指针。当然我们可以把函数指针作为参数传递到其他的方法,或者实例变量里面,从而获得极大的动态性。我们获得了动态性,但是付出的代价就是编译器不知道我们要执行哪一个方法所以在编译的时候不会替我们找出错误,我们只有执行的时候才知道,我们写的函数指针是否是正确的。所以,在使用函数指针的时候要非常准确地把握能够出现的所有可能,并且做出预防。尤其是当你在写一个供他人调用的接口API的时候,这一点非常重要。

方法的定义

在头文件 objc-class.h 中,有方法的定义 :

1 2 3 4 5 6 7
typedef struct objc_method *Method; struct objc_method { SEL method_name; char *method_types; IMP method_imp; };

这个定义看上去包括了我们上面说过的其他类型。也就是说,Method(我们常说的方法)表示一种类型,这种类型与selector和实现(implementation)相关。

最初的SEL是方法的名称method_name。char型的method_types表示方法的参数。最后的IMP就是实际的函数指针,指向函数的实现。

Class的定义

Class(类)被定义为一个指向struct objc_class的指针,在objc/objc-class.h中它是这么定义的:

1 2 3 4 5 6 7 8 9 10 11 12
struct objc_class { struct objc_class *isa; /* metaclass */ struct objc_class *super_class; /* 父类 */ const char *name; /* 类名称 */ long version; /* 版本 */ long info; /* 类信息 */ long instance_size; /* 实例大小 */ struct objc_ivar_list *ivars; /* 实例参数链表 */ struct objc_method_list **methodLists; /* 方法链表 */ struct objc_cache *cache; /* 方法的缓存 */ struct objc_protocol_list *protocols; /* protocol链表 */ };

由以上的结构信息,我们可以像类似于C语言中结构体操作一样来使用成员。比如下面取得类的名称:

1 2 3 4
Class cls; cls = [NSString class]; printf("class name %s/n", ((struct objc_class*)cls)->name);
发送消息与函数调用的不同

Objective-C的消息传送如下图所示 :

Objective-C MessageSystem

Objective-C的消息传送

发送消息的过程,可以总结为以下内容 :

  • 首先,指定调用的方法
  • 为了方法调用,取得 selector
源代码被编译以后,方法被解释为 selector。这里的 selector 只是单纯的字符串。
  • 消息发送给对象B
消息传送使用到了 objc_msgSend 运行时API。这个API只是将 selector 传递给目标对象B。
  • 从 selector 取得实际的方法实现
首先,从对象B取得类的信息,查询方法的实现是否被缓存(上面类定义中的struct objc_cache *cache;)。如果没有被缓 存,则在方法链表中查询(上面类定义中的struct objc_method_list **methodLists;)。
  • 执行
利用函数指针,调用方法的实现。这时,第一个参数是对象实例,第二个是 selector。
  • 传送返回值
利用 objc_msgSend API 经方法的返回值传送回去。

 

简单地从上面发送消息的过程可以看到,最终还是以函数指针的方式调用了函数。为什么特意花那么大的功夫绕个大圈子呢?1

这些年,随着程序库尺寸的扩大,动态链接库的使用已经非常普遍。就是说,应用程序本身并不包括库代码,而是在启动时或者运行过程中动态加载程序库。这样一来一方面可以减小程序大小,另一方面可以提升了代码重用(不用再造轮子)。但是,随之带来了向下兼容的问题。

如果程序库反复升级,添加新的方法的时候,开发者与用户间必须保持一致的版本,否则将产生运行时错误。一般,解决这个问题是,调用新定义的方法的时候,实现检查当前系统中是否存在新方法的实现。如果没有,跳过它或者简单地产生警告信息。 Objective-C中的respondsToSelector:方法就可以用来实现这样的动作。

但是,这并不是万全的解决方案。如果应用程序与新的动态程序库(含有新定义的API)一起编译后,新定义的API符号也被包含进去。而这样的应用程序放到比较旧的系统(旧的动态程序库)中运行的时候,因为找不到链接符号,程序将不能启动。这就是 win32系统中常见的「DLL地域」。

为了解决这个问题,Objective-C 编译得到的二进制文件中,函数是作为 selector 来保存的。就是说,不管调用什么函数,二进制文件中不会包含符号信息。为了验证 Objective-C 编译的二进制文件是否包含符号信息,这里用 nm 命令来查看。

源代码如下 :

1 2 3 4 5 6 7 8 9
int main (int argc, const char * argv[]) { NSString* string; int length; string = [[NSString alloc] initWithString:@"Objective-C"]; length = [string length]; return 0; }

这里调用了 alloc、initWithString:、length 等方法。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
% nm Test U .objc_class_name_NSString 00003000 D _NXArgc 00003004 D _NXArgv U ___CFConstantStringClassReference 00002b98 T ___darwin_gcc3_preregister_frame_info U ___keymgr_dwarf2_register_sections U ___keymgr_global 0000300c D ___progname 000025ec t __call_mod_init_funcs 000026ec t __call_objcInit U __cthread_init_routine 00002900 t __dyld_func_lookup 000028a8 t __dyld_init_check U __dyld_register_func_for_add_image U __dyld_register_func_for_remove_image ...

可以看到,这里没有alloc、initWithString:、length3个方法的符号。所以,即使我们添加了新的方法,也可以在任何新旧系统中运行。当然,函数调用之前,需要使用 respondsToSelector: 来确定方法是否存在。正是这样的特性,使得程序可以运行时动态地查询要执行的方法,提高了 Objective-C 语言的柔韧性。

Target-Action Paradigm

Objective-C 语言中,GUI控件对象间的通信利用 Target-Action Paradigm。不像其他事件驱动的 GUI 系统实现的那样,需要以回调函数的形式注册消息处理函数(Win32/MFC,Java AWT, X Window)。Target-Action Paradigm 完全是面向对象的事件传递机制。

例如用户点击菜单的事件,用Target-Action Paradigm来解释就是,调用菜单中被设定目标的Action。这个Action对应的方法不一定需要实现。目标与Action的指定与方法的实现没有关系,源代码编译的时候不会检测,只是在运行时确认(参考前面消息传送的机制)。

运行时,通过respondsToSelector: 方法来检查实现的情况。如果有实现,那么使用performSelector:withObject:来调用具体的Action,像是下面的代码:

1 2 3 4 5 6 7 8 9 10 11
// 目标对象 id target; // 具体Action的 selector SEL action; ... // 确认目标是否实现Action if ([target respondsToSelector:actioin]) { // 调用具体Action [target performSelector:action withObject:self]; }

通过这样的架构,利用 setTarget: 可以更该其他的目标,或者 setAction: 变换不同的Action。实现动态的方法调用。


 

1. C/C++语言中用回调函数(callback)的概念来实现程序的动态语义,一般该回调函数都是全局或静态的函数,使用Thunk的方法可以将类的成员函数作为回调函数来使用—利用平台相关的技术将对象实例(this指针)传递给调用端。

 

 

iPhone开发入门(8)--- 程序画面与控件调整

画面的构成

Cocoa Touch编程中,一个应用程序里面可以包含多个画面。通过列表选择来显示,或者通过下方的标签来显示,等等。CocoaTouch中将这样一个一个的画面成为 View。

如果只是创建一个View,不能实现一个完整的应用程序。这里,必须生成 Outlet 和 Action,这样才能将程序与 View 连接起来。比如取得View中配置控件的信息,更改其内容等。像这样,加入View中程序称为 Controller。或者针对与View的「ViewController」。Interface Builder中的「ViewController」就是用在创建 Outlet 和 Action 的「File's Owner」。

ViewController

 

View与Controller的关系

可以看出,多个View就要对于多个ViewController。简单起见,这里我们首先实现一个画面的应用程序。

控件调整

自动调整功能

auto

 

位置调节

用 Interface Builder 配置控件的时候,如上图所示会显示蓝色的虚线。这是建议你放置的最佳位置。有效地利用IDE提供的机能,可以很好地设计出造作简便的View。

调整控件文字大小

选择想要更改的控件,在Interface Builder菜单中选择「Font」→「Show Fonts」。如下图所示:

fonts

 

选择字体,大小

fonts

 

调整字体,大小

文字的大小不可能超过控件的大小。通过选择「Layout」→「Size To Fit」可以是字体大小匹配控件大小。

fonts

 

Size To Fit

各种控件

这里介绍一些常用的控件。都是通过用户的操作会引起Action的控件。

Round Rect Button

Round Rect Button就是一般的按钮,只是四角为圆角。配置在View上,后可以输入文字。

Round Rect Button

 

Round Rect Button的配置

选择 Action 的时候,「Touch Down」是当按钮被按下的时候发生的Action。

Round Rect Button

 

Round Rect Button的Action

Switch

Switch就是一个开关控件。具有开和关两个状态,每次切换的时候都会产生Action。

Switch

 

Switch的配置

Switch

 

初期状态设置

Switch

 

Switch的Action—Value Changed

比如,当开关控件的 Outlet 为 [outlet isOn],其Action可以定义为:

1 2 3 4 5 6 7 8 9
- (IBAction)myAction1:(id)sender { if ([sender isOn] == YES) { ... // 开关控件为ON时的处理 } }
Slider

Slider

Slider控件的配置

Slider

Slider控件的范围设置

Slider的Action为「Value Changed」,即是说当设定值变化的时候就会发生相应的Action。

Slider

Slider控件的Action

通过Slider控件的 Outlet 我们可以想下面的代码一样实现Action。

1 2 3 4 5 6 7 8 9
- (IBAction)myAction1:(id)sender { if ([(UISlider *)sender value] == 0.5f) { ... // Slider的值为0.5时的处理 } }

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值