1.有关于BOOL陷井方面有如下方面:
关于BOOL条件语句中的比较最好是与NO的值来进行比较,因为BOOL的 YES与NO值只是约定,并且编译器将BOOL认作8位二进制数据。若是不小心将一个长于1字节的整型值赋值给BOOL变量,那么只有底位字节将会用做 BOOL的值,假设底位字节刚好为0的话,那么BOOL的值将会是0,即NO值,所以关于比较方面推荐用变量值与NO值进行比较。
2.Objective-C中一些基础的语法问题:(重要声明:Objective-C就是C,不是什么其他的语言)
NS****命名规则:简单来说就是以NS为前缀加上匈牙利命名法。
#import 在 Objective-C中的使用情况:#import在 Objective-C程序中相当于#include在C/C++中的用法,但是前者在功能上面与后者是有一定的区别,简单说一点:前者可保证头文件只被 包含一次,而不管此命令在那个文件中实际出现(使用)了多少次。在c语言中,程序员通常使用机遇#ifdef命令的方案来避免一个文件包含另一个文件,而 后者又包含第一个文件的情况,在objective-c中,程序员使用#import实现这个功能。
NSLog()和@”字符串”在objective-c中的使用情况:
NSLog()函数在Objective-C中的作用相当于Console、printf()在c/c++语言中的作用。NSLog()还有时间 戳、日期戳和自动添加换行符(‘\n’)等一些Console与printf()不具备的功能。在Objective-C中@符号是其在标准c语言基础上 添加的特性之一,”"双引号中的字符串前有一个@符号,这表示引用的字符串应该作为Cocoa的NSString元素来处理。
NSString的部分功能有:
a.告知其长度;b.将自身与其他字符串进行比较;c.将自身转换为整型值或者浮点值。
在Objective-C中多参数方法的简要介绍:
多参数方法需要抓住的一点是后面的参数都需要写上名字。
在 Objective-C中加号、减号、中括号他们的意义与用法:
首先需要了解的一点是在 Objective-C中是没有public与private的概念的,即可以认为全部都是public。减号表示的是一个函数、方法、消息的开始。加号 则表示不需要创建一个类的实例,其他类就可以直接调用这个类中的函数(也可以说加号表示静态的成员函数)。中括号在 Objective-C中表示方法的调用(通常在 Objective-C中都是说”消息”)。下面一段转换关系就可以清楚的搞懂中括号的使用了:[[[MyCalss alloc] init:[foo bar]] autorelease]; 其转换为C#或者Java的语法如:MyCalss.alloc().init(foo.bar()).autorelease(); 这段转换关系非常的重要。
最后关于部分重要东西及个别问题的介绍:
1.id:
Objective-C有一种比较特殊的数据类型是id,可以理解为”随便”。等价于c语言中的void *;在Objective-C里一切数据都是以指针的形式保存,你获取到的就是这个对象在内存的位置,而id就是你知道的这个位置。
2.同一个数组可以保存不同的对象:
在Objective-C中数组(NSArray)里面可以保存各种不同的对象,通过以下例子来表示:{myArray<—|0: (float)234.33f 1:@”要玩娱乐” 2:(NSImage *) (图片资源) 3:@”世纪大光棍”}以上这个数组包含了4个元素,分别是浮点数、字符串以及图片。
3.BOOL、YES、NO:
在Objective-C中通常把YES等价于C#、Java、C、C++中的true,把NO等价于false,而实际上YES为1,NO为0,BOOL本身是一个char型数据(编译器认作为8位二进制数据)。
4.IBOutlet、IBAction是什么东西,为什么总能看见:
在语法中IBOutlet、IBAction没有太大作用,若是希望在Interface Builder中能看到这个控件对象,那么在定义的时候前面加上(IBOutlet),在IB里面就能看到这个对象outlet,如果希望在 Interface Builder里控制某个对象执行某些动作,就在方法前面加上(IBAction).实际上IBOutlet、IBAction就跟void是一样的。
5.nil的意义是什么:
在Objective-C里NULL(空)就是用nil来表示,nil表示空指针。
6.@”字符串”与”字符串”有什么不同或者区别:
“字符串”是C的字符串,@”字符串”是把C的字符串转换成NSString的一个简写.在需要NSString的地方才需要这个转化,例 如:NSLog里面.在需要C string的地方,还是用”字符串”的形式.另外,@”"这个转换是不支持中文的,例如NSLog(@”字符串”);是一定输出不了中文的.
3.函数原型和声明方式:
首先:先行下短线—即是“减号”,每种方法的名称、方法的返回值的类型和某些参数。先行短线后面接着就是返回值.
中缀符:Objective-C中有一种名为中缀符(infix natation)的语法技术,方法的名称及其参数都是合在一起的.如:可以这样调用带一个参数的方法:[circle setFillColor:kRedColor];带两个参数的方法调用如下:[textThing setStringValue:@"hello there"color: kBlueColor];setStringValue:和color:实际上是参数的名称(实际上是方法名称的一部分,后面在详细介 绍),@”hello there”和BlueColor是被传递的参数.
冒号:有参数的方法时通常是方法的名称结尾处是冒号,冒号之后紧接着就是这个参数类型及其参数名.冒号之后的圆括弧中是参数的类型.在无参数的方法 中在方法的名称结尾处通常是不需要添加冒号的而是分号.这里是非常需要注意的,有时候就是因为在没参数的方法后面加上了冒号而导致程序出现了错误信息是没 有意义的初级错误.
总结出以下情况为函数原型及声明的一般形式: - (方法的类型) 方法的名称: (参数的类型) 参数的名称; 或者是: – (方法的类型) 方法的名称;
最后:声明完成后必须在最后一句加上@end来告诉编译器类的声明已经完成.这也是很重要的一处基础.
4.实现代码的指令及需要注意的地方:
编译器同过@implementation这个指令来为某个类提供代码,类名出现在@implementation之后,该行的结尾处没有分号,因为在 Objective-C编译器指令后不必使用分号.在@implementation指令中各个方法的定义,不必按照在@interface指令中的顺序 出现,甚至可以在@implementation中定义那些在@interface中没有相应声明的方法。可以把他们(这里的他们是指的是:在 @interface中没有相应声明的方法)看成私有的方法,仅在类的实现中使用。在这里有一点需要注意:在Objective-C中是不存在真正的私有 方法.也就是说单独在@implementation指令中定义的方法是能从外部进行访问的,这算是Objective-C动态本质的副作用.传递隐藏的 参数是另一种间接操作的事例.Objective-C运行时(runtime)可以将不同的对象当成隐藏的self参数传递,所以哪些对象的实例变量发生 更改时,运行时也可以进行相应的更改.
说明:用户运行应用程序时,Objective-C运行时是支持这下应用程序(包括我们自己的应用程序)的代码块,运行时执行非常重要的任务,如向对象发送消息和传递参数.
5.实例化对象:
Shapes-Object中最后的、非常关键过程,在该过程中,我们创建形状对象,例如红色的圆形和绿色的矩形,这个过程的专业术语就叫做实例化 (instantiation).实例化对象时,需要分配内存,然后这些内存被初始化并保存一些有用的默认值,这些值不同于你在 获得新分配的内存时得到的随机值,内存分配和初始化完成后,就闯将了一个新的对象实例.这里有一点需要注意:由于对象的局部变量特定于该对象的实例,因此 我们称它们为实例变量,通常简写为ivars.
为了创建新对象,我们需要向相应的类发送new消息,因此接收并处理完new消息后,我们就会得到一个可以使用的新对象实例.
Objective-C有一个极好的特性,你可以把类当成对象来向类发送消息,这种便捷的行为不局限于某个特定的对象,而是对全体类都通用,这种消 息通常用在创建新对象时,如果需要创建新的circle对象,请求Circle类创建新对象比请求某个现有的circle对象更合适一些.
第4章节:
1.继承:~~@interface Circle: NSObject~~冒号后的标识是需要继承的类,因为可以从非类中继承对象.Cocoa,NSObject继承对象,因为NSObject提供了大量有 用的特性(当继承一个已继承自NSOvhect的类时,你也能获取这些特性).Objective-C中继承不支持多继承,想在Objective-C中 获得多继承的优点,那么就要使用Objective-C的其他特性,如:分类和协议,在本书12、13章有介绍.
2.重构:移动和简化代码的方式称为重构,这在OOP社区中是一个非常时尚的话题.进行重构时,你通过移动某些代码来改进程序的架构,正如我们在这 里把程序中重复代码删除,而不改变代码的行为或运行结果一样,通常的开发周期包括想代码中添加某些特性,然后通过重构删除所有的重复的代码.通常,在面向 对象的程序中添加某些新特性后,程序反而变得更简单,你可能对此觉得很奇怪,这就想我们添加了Shapes类后所出现的情况.
说明:因 为继承在子类和超类只见建立了一种”is a”(是一个)关系,所以NSObjec的实例变量称为isa.即Rectangle是一种Shape,Circle是一种Shape,使用Shape的 代码也可以使用Rectangle或Circle来代替.使用更具体种类的对象(Rectangle或Circle)代替一般类型(Shape),这种能 力称为多态性(polymorphism),它在希腊语中形象地表示”很多形状”.
小心易碎:编译器使用”基地址加偏移”机制实现奇妙的功能.给定的对象基地址,是指第一个实例变量的首个字节在内存中的位置.通过在该地址加上偏移 地址,编译器就可以查找其他实例变量的位置.如:如果圆角矩形对象的基地址是0×1000,则isa实例变量的地址是0×1000+0,即位于 0×1000位置,isa的值占4个字节,因此,下一个实例变量fillColor的起始地址位于4个偏移地址之后,即位于0×1000+4位置,或写作 0×1004,,每个实例变量与对象的基地址都有一个偏移位置.如果访问方法中的fillColor实例变量,编译器生成代码并得到存储self的位置 值,然后加上偏移值(在本例中为4),得到指向存储变量值的位置.随着时间的推移,这也回产生一些问题,现在,在编译器生成的程序中,这些偏移位置是通过 硬编码实现的.尽管苹果公司的工程师希望向NSObject中添加其他的实例变量,但他们无法做到,因为这样做会改变所有实例变量的偏移位置.这被称为脆 弱的基类问题(fragile base class problem).通过在Leopard中引入新的64位Objective-C运行(它使用间接寻址方式确定变量的位置),苹果公司解决了这个问题.
3.重写方法:有时候为了在类中引入某个独特的特性,需要添加新方法.还有时候可能需要替换或增强由这个新类的某个超类所定义的现有方法.(使某方法为空,当方法中的子类都各自实现了自己的功能方法时,就说方法实现了重写).涉及关键字有:super(超类)。
说明一点:重写方法时,调用超类方法总是一个不错的选择,这样可以实现更多的功能.通过用调用super来实现机会,这样可以 保证能够获得方法的所有特性。
第5章节:
1.什么是复合:将多个组建组合在一起配合使用,从而得到完整的作品.这里需要注意的一点是:从严格上来讲,只有对象间的组合才能叫做复合,诸如int,float,enum和struct等基本类型都被认为是对象的一部分.
提醒:如果类中没有包含人和实例变量,就可以省去代码中的花括号。
需要记住的有:通过NSLog()可以使用%@格式说明符来输 出对象.NSLog()处理%@说明符时,它会询问参数列表中相应的对象以得到这个对象的描述.从技术上说,就是NSLog()给这个对象发送描述消息, 然后对象的description方法生成一个NSString并返回.之后NSLog()在其输出中包含这个字符串.在类中添加description 方法就可以自定义NSLog()如何输出对象.
2.存取方法:存取方法(accessor method)是用来读取或改变对象特定属性的方法。类存取方法称为setter方法。另外一种存取方法就getter方法。getter方法为使用对象的代码提供了读取对象属性的途径。
3.修改方法(mutator)是用来改变对象状态的方法。需要说明的一点:在其对象的属性进行操作时,应该始终使用对象所提供的存取方法,永远不要直接改变其对象属性的数值。存取方法是程序间接工作的另一个例子。
4.防御式编程:在存取方法中使用通用代码来检查实例变量的数组索引,以保证它是有效数值,若是超出了有效范围,那么程序就会输出错误信息并且退出,那么 该段代码就是所谓的防御式编程.(编程者需要考虑种种可能出现的问题,并且设置异常抛出机制,引申到编程者换位思考的问题上,就可发现出程序所欠缺的部分 待优化的功能)。
注意:继承的适用范围,切记不要用复杂的系统来继承包含在本系统中的模块,如:简单来说父与子的血脉继承关系是单向的,所以继承也需要考虑适用范围问题。
第7章节-深入了解Xcode:
1.使用编辑器的技巧与诀窍:
Xcode会在开发者键入代码法过程中给出建议,这是Xcode的代码提示功能,通常叫做代码自动完成功能.
Xcode中的快捷键:command键加上“[”、“]”两个键的功能是把当前光标位置的一行代码进行整体左移跟整体右移。command键加上“/”则为把当前光标位置的一行代码进行行注释。
2.括号匹配:输入程序代码时,也许在输入了某些字符(如”)”、”]”或”}”)后屏幕会有些闪烁,这就是Xcode在告诉在这里与之对应的开括号在哪里.这就是Xcode的”括号匹配”,也可以双击某个分隔符,Xcode会选定它以及与它匹配的括号之间的全部代码.
3.批量编辑:选择File->Make Snapshot(快捷键command-control-S),Xcode会记住项目的当前的状态.先在你就可以随心所欲的”破坏”你的项目.如果意识 到犯了一个很严重的错误,那么可以通过File->Snapshots来恢复你的快照.在这里若是要做什么项目上的重大的修改的时候都推荐备份一份 代码跟保留一份修改当前一个完整版本的代码快照.(说明:备份代码的重要性,因为在实际上快照被存储在一个磁盘镜像中,它存放 于~/Library/Application Support/Developer/Shared/SnapshotRepository.sparseimage.有时候这个磁盘镜像可能会被损坏 (也许因为生活压力太大了),这时Xcode会向你报告一个“神秘”的错误:Snapshot Failed:A project snapshot cannot be created.如果开发者看到了这样的错误,请尝试删除这个景象并重新启动).
4.Xcode中的查找与替换功能:此功能在子菜单Edit->Find中.里面有几个非常方便的选项.Find in Project可以在项目的所有文件里进行查找和替换.其实这种查找与替换工作并不是很好使用,如:若是开发者只是想重命名函数中的变量,那么它做了过多 的操作(因为它可能会改变整个文件中的变量名称),而开发者想重命名一个类时,它又无法办到,另外重要的一点,它不能重命名源文件.<在这里又有两 个功能可以弥补这些不足:1.可以简称为Edit all in scope,开发者可以选定一个符号,如局部变量或参数,然后选择Edit->Edit all in Scope,然后在开发者输入时,所有该符号出现的地方都会立即更新,这不但是进行大量改动的快捷方式,而且操作是看起来很”酷”,输入完毕之后,只要在 源文件编辑窗口单击其他地方,就会离开Edit all in Scope模式。2.“内置重构工具”,若开发者有一个GUI程序,它甚至能够深入到nib文件的层次进行修改操作,在需要修改的文件中设置插入点,然后 选择Refactor,为了保险需要确保Snapshot复选框被选中,单击Preview之后,Xcode会分析出要做什么,并将结果展示出来.可惜的 是,重构并不能重命名注释中的文字,所以类末注释、Xcode生成的头文件注释或者任何开发者编写的文档注释都需要手工编辑,开发者可以使用查找和替换功 能来简化这一过程.
5.代码导航:这里参考博客园中的emacs快捷键。
7.任意搜索:Xcode项目窗口的右上角右一个搜索框,Xcode通过开发者输入的字符来过滤浏览器中显示的内容.要使用这个索搜框,首先要在 Groups&Files列表中选定任意项,如Source文件夹,然后浏览器会显示该文件夹里的所有源文件。在搜索框中输入文本可以对这些文件 进行筛选。
8.芝麻开门:假如开发者正在看某个源文件,看到了文件上方的#import,如果能够徐素打开这个头文件而不用鼠标点来点去,是不是会很方便呢?这个非 常容易:只需要使用command+shift+o键就可以弹出一个对话框,然后在这个对话框中搜索栏中输入开发者看到的头文件名,然后在下面弹出来的信 息中选取适合的文件即可查看此文件内容.
9.书签:在Xcode中与许开发者在代码中插入书签,在代码中可能又一些需要引起人们注意的地方,如:在代码某一个区域以后需要修改优化等操作的 话,那么开发者在这里做一个标记的话,到动手操作的时候就可以快速定位此处了。做法为:首先在源文件中放入插入点或者选定一个文本区域,然后选中 Edit->Add to Bookmarks,或者使用默认的快捷键command+D(与Safari中的一样),最后当出现提示的时候,输入书签名称即可.
10.集中注意力:紧挨着源代码的左边又两个空列,它们当中左边较宽的一列叫做边列(gutter),靠右较窄的一列叫做焦点列(focus ribbon):焦点列的作用能够让开发者在浏览代码段时集中注意力.焦点列的灰度:代码嵌套得越深,在它旁边的焦点列中,灰色也会越深.这种颜色编码能 够使代码的复杂程度一目了然.用鼠标悬停在焦点列上时会突出显示相应的代码段,也可以点击焦点列来折叠相应的代码段。
11.开启导航条:在代码编辑器的顶部右一个小控件条,也就是导航条.这里面的很多控件可以让开发者快速地在项目中的源文件之间进行切换.关 于#pragma的说明:“Pragma”源自希腊单词,意思是“行动”.#pragma指令将Objective-C常规代码之外的信息或说明传递给编 辑器和代码编辑器.通常,Pragma是被忽略的,但它在一些软件开发工具中可能有其他含义,如果某个工具并不知道pragma是什么,则应该“点头微 笑”并且忽略它,而不是生成警告或错误信息.
12.研究助手(Research Assistant)的浮动小窗口会根据逆在Xcode力的交互操作来更新所显示它的内容,要打开研究助手,选择菜单Help->Show Show Research Assistant。
13.文档管理程序:开发者想直接访问苹果公司官方API文档,最快的方法是按住Option键并双击某个符号,这是查找该符号相关文档的快捷方 式.需要在Xcode其他窗口甚至是网页浏览器中打开文档也很容易,只需要按住control键并单击(或右键单击)文档区域来打开菜单即可.
14.调试:系统的在代码中查找程序哪里出错的过程叫做调试(debugging)。
15.暴力调试:最简单的调试方式就是暴力调试,暴力调试是指在程序中放入输出语句(如NSLog)来输出程序的控制流程和一些数据值.
16.Xcode调试器:Xcode还有一个调试器,调试器是位于开发者编写的程序和操作之间的程序,它能够中断开发者的程序,使之在运行中停止. 这样就可移检查程序的数据及其修改程序.开发者也可以单不执行逐行进行检查程序.说明:Xcode使用的调试器就是GDB,GDB是GNU项目的一部分, 它可以在很多不同平台上使用.若开发者愿意,也可以通过命令行来运行它,GDB有者完善的文档系统,尽管它的文档有些难于理解并且网络上流传者好几个版本 的GDB教程.
17.精巧的调试符号:打算调试程序时,需要确保开发者正在使用Debug生成配置.可以通过Xcode工具栏中的弹出菜单Active Build Configuration来检验.Debug配置告诉编辑器发出额外的调试器符号,而调试器通过这些符号可以知道程序在什么地方都有写什么.同时,还要 确认程序是用调试器来运行的,在Xcode里有两种运行程序的方法,选择菜单Run->Run或者按快捷键command+R,将会不使用调试器运 行程序,若要使用调试器,选择菜单Run->Go(Debug)、Run->Debug,或者按快捷键command+Y.说明:多线程编程 是一种同时处理多个执行流的编程方式,正确应用它是很困难的,通常,多线程编程所产生的错误非常难于找到,如果有人告诉你多线程很容易,那么他们不是被骗 了或者试图向你推销什么东西。下面介绍是4个控件:第一个控件看起来cd机的开始按钮.,安继续键可以使用command+option+p。单击它之 后,程序会接着运行到遇见下一个断点,然后结束或者崩溃.第二个控件看起像跳过按钮使用的快捷键是command+option+O.单击这个控件之后它 就会执行下一行,然后又会吧程序控制权交还给开发者,第三个按钮,向下指向一个点的箭头,它是跳入按钮(也可以使用快捷键:键盘右上角的 键+command+I),这个按键的功能就是跳入方法内部,显示其代码,最后箭头设置在代码的起始位置.第四个按钮是跳出按钮,快捷键是 command+右上角的键+T,单击它会终止当前运行的函数并且程序会停在调用程序的下一行代码.若是在试验当中,那么就没有必要在试验当中按这个键 了。再下一个按钮(又一个喷雾罐的方框)是用来打开Xcode调试窗口的,再下一个按钮用来打开GDB控制台,开发者可以再这个调试器种直接输入调试命 令.最后一个控件是显示调用栈的弹出菜单,调用栈是当前处于活动状态的函数的集合,如果A调用B,B调用C,那么C就位于栈的顶部.再更为复杂的程序中, 这种调用栈也称为栈跟踪。
18.检查程序:当开发者在程序的某个部分设置断点或者分步执行时,说明开发者向了解程序状态–变量的值.在Xcode种又数据提示功能.
第八章节-Foundation Kit快速教程:
Cocoa的框架由两个不同的框架组成的:Foundation Kit和Application Kit组成,后者包含了所有的用户接口对象和高级类.main()函数的创建通过alloc、初始化通过init
1.有用的数据类型:a.范围的作用、b.几何数据类型。a.第一个结构体是NSRange,第一种是直接给自段赋值;第二种是应用c语言的聚合结构赋值 机制;第三种是Cocoa提供的快捷函数NSMakeRange();如:x.NSRange range;range = 17;range = 4;y.NSRange range = {17,4};z.NSRange range = NSMakeRange(13,15);b.NSPoint来表示一个点,NSSize来储存长度和宽度.Cocoa提供一个由点和大小复合而成的矩形函 数数据类型:其快捷函数为:NSMakePoint();NSMakeSize();和NSMakeRect();这里需要说明:Cocoa的这些数据类 型是C的struct类型而不是对象呢?原因归结起来就是因为性能。程序(尤其是GUI程序)会用到许多临时的点、大小和矩形来完成他们的工作,还记得 吧,所有的Objective-C对象都是动态分配的,而动态分配是一个代价较高的操作,它会消耗大量的时间,所以将这些结构创建成第一等级的对象都会在 使用过程种增加大量的系统开销.
2.字符串:Cocoa种的NSString有很多内置方法,让字符串个的处理边的简单很多.a.创建字符串,通过接受格式字符串和一些参数来输出格式话 的结果NSString和stringWithFormat:方法就是通过格式字符串和参数来创建NSString的;b.类方法,;c.关于大小、d. 比较策略、e.不区分大小写的比较、f.字符串内是否还包含别的字符串.
4.字典NSDictionary:字典就是关键字及其定义的集合。Cocoa种有一个实现这种功能的集合类 NSDictionary.NSDictionary在给定的关键字(通常是一个NSString字符串)下存储一个数值(可以是任何类型的对象)。然后 开发者就可以用这个关键字来查找相应的数值,所以开发者有一个存储了某人所有联系信息的NSDictionary.那么开发者就可以对这个字典说,“给出 关键字home-address下的值”,说明:字典(也被称为散列表或者关联数组)使用的是键查询的优化存储方式,它可以立即找出要查询的数据,而不需 要便利整个数组进行查找,对于频繁的查询和大型的数据集来说,使用字典比数组要快得多,实际上字典非常快。
我在一年前因需要将RedPhone项目从Android移植到iOS而首次接触Objective-C。大约一个月前我负责的部分(后端:声音,网络,加密部分)已经完成。我们正等着外部的安全审查,同时在内部继续完成UI工作的过程中并未发现任何后端的bug(言外之意开发质量高)。在RedPhone最终发布后,我对于这次工作中哪些做错了,基于android和ios的代码都有哪些不同等等做了讨论。今天想探讨一下去年过程中关于Objective-C的一些体验。
一年前我从未接触过Objective-C.(一行代码也没看过)我是名C#开发。现在我认为自己是名中级Objective-C开发:
虽然还有很多知识上的漏洞但我在现在使用这门语言已算是得心应手。比如,大家都知道不能把nil赋给数组和其他的集合类型,但就在上周我还不知道可以直接插入NSNull来实现。当然了,我只是假设大家只是不会把nils赋给某个集合。(意味着大部分人也不知道这个workaround)
当我讨论语言时,我听起来会倾向于比较挑剔,所以请记住这篇文章不是指控Objective-C无法有效的处理问题。这篇文章是关于我使用Objective-C的经验,它和其他语言不同的地方,以及我遇到的问题。这是我的观点,因为编程领域充满了关于哪种语言更好的完全对立的观点(e.g. 动态 vs 静态类型编程语言),你可以相信,这仅仅是以一对多。一个相关的引用:
仅有两种语言:一种是大家抱怨的,另一种是没人使用的。 —— Bjarne Stroustrup
概述
Object-C是C语言的超集,所以很自然的让我注意的第一件事是它哪里和C不同。基本上你在以Obj-C风格写代码时唯一做的类似C的事情(C-ish stuff)是使用头文件和使用前进行类型声明。
奇怪的是,Objective-C使我想起的更多的不是C而是Visual Basic 6。至少,是在我每日使用所遇到的问题上。Obj-C和VB6两者都 喜欢让程序保持 运行下去(即使出错了,译者注)。这是不是个奇怪的现象?程序可能会错过打破当前状态的问题。另外,两种语言都使用引用计数来清理内存,这会随着你的深入造成更多内存泄露。永远警惕引入循环引用。最后,两者都没有泛型类型。把这些话都直直的装入你的脑子里,否则你接下来的日子会很难过。
还有另一件Obj-C与VB的包袱,例如,在Objective-C中许多对象有 “core foundation”、 “new style” “NeXTSTEP”的形式,而自动引用计数技术早在两年前就已渗透到整个社区了,当然了,还有C的子集。所以,大多数情况下我们可以忽略这些历史包袱,但它们的出现,我并不是暗示说Obj-C与VB是相同的编程语言,它们有很大不同,首先,语法就有很大的不同。
语法
Objective-c的语法很特别。传递一个消息(如调用个方法),你需要用方括号把接收消息的对象以及消息括起来。消息由参数名和值组成,因此不能像 list.Insert(“test”, 0)这样写,而是这样 [mutableArray insertObject:@"test" atIndex:0]。
当调用方法时给每个参数取个名字可以使代码非常清晰,但是不可避免的增加了代码量。总的来说这是件好事。另一方面,我认为让方括号在目标对象之前开着,而不是在目标对象之后(像Java, C#, C++)是个错误。这使你更难确定你在一个表达式的位置,并且使一个简单的序列变成了深层嵌套。扩展一个表达式是个反复的事情,因为你必须回到行首添加 [。 当前输入 ] 时XCode会尝试猜测 [ 的相对位置,但是它老是搞错以致变得更糟。
幸运地是,Objective-c有些语法糖可以让你在一般情况下摆脱嵌套。点记法 可以让你重写简单的getter表达式,如[a value] 重写为 a.value。setter, 数组索引,字典查找也有类似的语法糖。他们确实为减少冗长的代码创造了奇迹。
Objective-c 也有容器了,这个是关于这门语言我真的很喜欢的第一件事。你想要个字典?只需要输入@{key:val,key2:val2,…}, 没有自动包装,因此你不得不在每一处添加@符号,像@{@”a”:@1,@”b”:@2,…}, 但是相比于人们过去不得不做的(大量地NSNumber numberWithInt:,以null结束的NSArray arrayWithObjectsfor:值,another key值,最后不幸中的万幸NSDictionary dictionaryWithObjects:forKeys:)这只是很小的代价。
我没有预料到Object-C会有匿名函数的功能,但是它确实有(它们叫做块)。块语法因为缺乏推理类型语法,所以你不得不写^(int x){return x*x;}用来替代 x=>x*x,但是确实足够简明可以被使用。
另外一个对于Object-C语法来说的必要点就是前缀使用“+”和“-”用于区分静态方法和实例方法(错误的…信息)。你将会很快的习惯它,虽然它的这些差别看起来有些滑稽。
类型系统
如果让我说一句我恨Objective-C的地方,那一定就是类型系统了。它很难表示出很多有用的类型(例如没有泛型)并且实际上它也不能检查你的工作(甚至在运行时)。虽然它在感觉上很动态,但是我更喜欢静态的强类型。
一开始,Objective-C没有null(但是C语言有,虽然你不常用)。替代的,Objective-C有nil,它很像null除了当你不小心在程序里使用它的时候它不会使程序停止。发给nil的消息不会抛出异常,它仅仅只返回了一个nil并且实际上不作任何的评估。
我是真的真的不喜欢nil. 例如,假设你写了像构造函数的方法,它有一个叫SuperImportantSecurityChecker的参数。
你坚持参数不是nil,因为对不合逻辑的不被执行进行安全检查是很糟糕的。
你也可以写一个测试,故意导致安全失败,并且检查下确实是失败了。你做的很好,因为你忘记了用SuperImportantSecurityChecker参数的值初始化SuperImportantSecurityChecker了。像这样的错误会几年都不会被发现,这多亏了nil。
另一个例子,当程序已经出错了,但是会继续运行,令人吃惊的竟然是缺少运行时类型检查。例如,如果你写NSMutableArray* v = [NSArray array],Objective-C会高兴的将你的可变数组的指针指向一个不可变的数组(你以前甚至没有看到编译报警,因为NSArray.array返回id, 现在它返回 instancetype)。当你尝试去调用给“可变”数组添加对象的方法时,程序会崩溃,错误提示为“selector not found”。这个并不像不可期的nil那样糟糕,nil会悄悄地丢弃要添加的项而不是崩溃,但是查找这些错误是很恼人的。
当在块上工作时缺少运行时检查是非常恶心的,因为语言允许你过度自由的指定输入类型。这种写法看起来多么诱人,[array enumerateObjectsUsingBlock:^(NSNumber* obj, NSUInteger index, BOOL *stop) { ... }];,根据你的期望,thatobjshould通常是一个数字而不是generalid,但是有时候你会搞砸它并且不得不追踪问题所在。我已经采取了在大多数块开始前加一行strewingassert([obj isKindOfClass:[WhateverType class]])来先发制人的捕捉这些错误。
另一个值得注意的Objective-C会默默舍弃的是,如果你忘记了特定的编译选项。你或许已经知道不指定“- Objc”会导致分类方法在运行时不被接受,尽管不会在编译时被发现,但是你知道不包含-fobjc-arc-exceptions”会导致有异常抛出时ARC不能正确的进行清理吗?根据约定 强烈支持你不要捕获异常是合理的,并且速度好处显然不小,但是,苹果让他们的语言的默认执行不正确的行为着实让我受宠若惊。
基本上,我时常感觉Objective-C的类型系统是在挑战我。我也尝试写一个安全的voip应用来保护你免受你的压迫性的政府,但是我在用的语言实际上被设计成升级微不足道的错误来折中,而不是立马崩溃。我觉得,我将从不会犯低级错误,因此没有人会死,对吗?这是完美的。或者有些人受虐待。。。我也许也会检查它十多次。。。
XCode
语言的开发环境,是很影响你对该语言的感受的。开发Objective-C程序可以选择苹果公司的XCode工具,我个人比较习惯于使用XCode。此外还可以选择JetBrain的AppCode开发环境来开发,虽然这个工具是收费的,但说实话我不是很喜欢,没用多久就丢弃了。
如果我给XCode评个分的话,满分5分我给3分,算是中规中矩吧。XCode有许多细节做得很棒,比如, 这个fantastic ,它会高亮显示匹配的花括号(闪烁黄色高亮),还有方法自动补全之后,有行内气泡提示,这也非常好用。不过也有许多不足之处。
奇怪的是,我主要抱怨的是项目文件的格式。我发誓,它设计就是为了引起合并冲突。我不知道是否UUID与各个条目、重复信息,或者每行多种信息相关。。。但我不记得最后一次我合并,而没有手动修复&*(&ing项目文件。
有趣的是,对项目经常性的崩溃有些本末倒置。修复项目最快的方法是恢复它,然后通过把对应的文件夹拖进项目中重建组,来修复包含源代码的组(这么做是有效果的,因为源码文件合并是正常的)。这个规律的重建操作保持你的组与文件系统同步,因此你的项目不会在像github一样的外部环境中看起来一团糟。多么“方便”啊。
我使用XCode的下一个问题是自动补全。它并不擅长这个。特别是当你在上面已经有了写了一半的代码,在你编辑时总是可以保证看到部分结果。这会诱使你认为基本的方法不存在,因为你正在盯着一个不包括它们的补全列表。XCode自动补全的另一个情况是当你使用点语法时明显不好:它基本没工作过。输入一个空格,你得到很多的结果,包括读取器,但对应输入一个点,你将可能看不到任何结果。令人沮丧。
我对XCode的最后一个抱怨,也是值得提一下的,是有限的重构功能。在重命名变量和一些其他东西时,会受限制,而且也没什么效果。这真的很慢(我见过花费几分钟的情况),另一半情况就是XCode崩溃。老实说,如果你想要做重构的话,你应该仅仅安装AppCode。要么用那个,要么习惯忍受find+replace。