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

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

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

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

创建Xcode工程

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

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

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

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

源代码文件

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

XIB文件

XIB文件是记录应用程序画面构成和配置的文件。位于Resources目录下。用Cocoa Touch来开发程序,编辑XIB文件是最基本的。编辑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,可以更加深入的了解各种组件的使用。

由菜单选择程序库

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

配置控件

标签

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

Label控件

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

Text Field

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

Text Field

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

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

Inspector

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

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

KeyBoard Type

Segmented Control

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

Segmented Control

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

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

编译与测试

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

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

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

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

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


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

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

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

确认操作与处理

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

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

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

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

Outlet与Action

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

Outlet

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

Action

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

编写程序

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

生成Outlet

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

File's Owner中表示程序的ICON

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

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

追加Outlet

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

生成Action

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

追加Action

与控件关联

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

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

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

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

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

创建文件

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

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

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

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]]; }

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

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的函数调用

函数调用首先用 [ ] 来包住实现代码。函数调用的对象叫做接收器(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的函数调用

「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的发送消息

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

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的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的消息传送

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

  • 首先,指定调用的方法
  • 为了方法调用,取得 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」。

View与Controller的关系

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

控件调整

自动调整功能

位置调节

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

调整控件文字大小

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

选择字体,大小

调整字体,大小

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

Size To Fit

各种控件

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

Round Rect Button

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

Round Rect Button的配置

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

Round Rect Button的Action

Switch

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

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的Action为「Value Changed」,即是说当设定值变化的时候就会发生相应的Action。

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时的处理 } }

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

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

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

  2. iPhone开发入门(10)— 设备上运行程序

    博主:易飞扬 原文链接 : http://www.yifeiyang.net/introduction-to-iphone-development-10-devices-running-the-pro ...

  3. 观点:再见Objective C?程序员眼中的Swift

    对于苹果开发者来说,如今已经进入了"Swift时代".虽然编程语言Objective C备受喜爱,不过它作为苹果主流编程语言的日子已经所剩无几.随着WWDC开发者大会的落幕,Swi ...

  4. GPU Saturday技术沙龙:OpenCL程序员眼中的下一代APU架构

    摘要:GPU Saturday技术沙龙在北京·3WCoffee成功举办.本次活动邀请AMD资深技术人员及清华大学项目研究员就AMD最新的GCN架构.GPU加速计算在挖掘比特币.典型图像算法.深度神经网 ...

  5. Unity3D iPhone开发入门 (转)iPhoneSettings.

    From: http://www.j2megame.com/html/xwzx/ty/2363.html 作者:Unity3D大中华社区 日期:2011年07月13日 来源:Unity3D大中华社区 ...

  6. Unity iPhone开发入门

    可能开发iphone的同学不多,但里面讲到的一些优化思路同样也能适用于pc开发,大家可以借鉴.本人翻译水平有限,欢迎指正呵呵! Unity iPhone开发入门 (一) 为iPhone开发 为iPho ...

  7. 程序员眼中的漫威!太有才了!

    程序员眼中的漫威!太有才了! 漫威宇宙其实也就讲了一件事.整个宇宙就好比个git项目,其中有一群叫做美国队长,钢铁侠,惊奇队长,浩克,索尔等人在维护这个项目,兢兢业业的修理bug. 某一天出现了一个天 ...

  8. Python概述:C++程序员眼中的Python

        在C++的程序员眼中,脚本是个奇葩的逻辑.当然也可能我比较奇葩.不过从Lua走过来,python的理解也不那么难.记录一下刚学python要注意的东西,也算是个快速入门吧.如果你是从C++过来 ...

  9. 几张趣图带你了解程序员眼中的世界

    整理自:新浪微博@程序员那些破事 HTML&CSS ▼ 索引 ▼ 双链路冗余备份 ▼ 传说中的解耦 ▼ 过度封装 ▼ 明文vs对称加密 ▼ 程序员眼中的世界 ▼ ❈ 今天就周五啦,鬼知道我到底 ...

最新文章

  1. 一个Bug,让我发现了Java界的.AJ(锥)!
  2. 终于也为自己开了技术博客
  3. 在Asp.net core 项目中操作Mysql数据库
  4. c++进制转换代码_轻松实现C/C++各种常见进制相互转换,你还不会你就落后了
  5. discuz安装_手动搭建 Discuz! 论坛
  6. matplotlib绘制多张图、多子图、多例图
  7. CORS预检请求详谈
  8. 使用定时器判断确保某个标签有值才执行方法, 控制js代码执行先后顺序
  9. python set去重 字典 计算求和_python字典set方法的特殊方法
  10. CentOS 6.5 x64 Redis 安装
  11. 在editor模式下遍历unity3d builtsetting中的场景
  12. python dict下标_牛鹭学院:必须掌握的Python数据结构知识
  13. [C编程在Linux上]用printf做彩色日志记录
  14. 软件测试方法(单元测试、集成测试、系统测试、确认测试)
  15. matlab chi2gof,chi2gof函数里的检验值P为什么总等于NaN呢
  16. mysql user.myd_“./mysql/user.MYD”未找到(ERRCODE:13 - 拒绝权限)
  17. 邓白氏编码的申请流程
  18. python画三角形
  19. 伴随着Web标准发展
  20. 什么是云连接?云学院带你了解华为云连接知识

热门文章

  1. 时间复杂度计算方法以及常见的时间复杂度
  2. C#“调用的目标发生了异常”之终极解决办法
  3. vs2017调用目标发生异常
  4. excel操作 - xlwings模块
  5. python是c语言_python是c语言开发的
  6. 20165214 第九周学习任务
  7. 如祺出行:广汽与腾讯的算盘
  8. SpringBoot重点详解--log4j.properties配置详解与实例
  9. Codeigniter 4基础教程(2)-- 版本差异,模板,一个form例子
  10. NTLM及Kerberos认证流程