« 最近一些心得 | 返回首页 | 动态字体的贴图管理 »

Objective-C 的对象模型

最近稍微学习了一点 Objective-C ,做笔记和做编码练习都是巩固学习的好方法。整理记录脑子里的新知识有助于理清思路,发现知识盲点以及错误的理解。

Objective-C 和 C++ 同样从兼容 C 语言开始,以给 C 语言增加面向对象为初衷,他们的出现的时间都很类似(1983 年左右)。但面向对象编程的源头却不同:C++ 受 Simula 和 Ada 的影响比较多,而 Objective-C 的相关思想源至 Smalltalk ,最终的结果是他们在对象模型上有不小的差异。

以我这些天粗浅的了解,Objective-C 似乎比 C++ 更强调类型的动态性,而牺牲了一些执行性能。不过这些牺牲,由于模型清晰,可以在今天,由更先进的编译技术来弥补了。

我对 C++ 的认知比 Objective-C 要多的多,所以对 C++ 开发中会遇到的问题的了解也多的多。在学习 Objective-C 的过程中,我发现很多地方都可以填上曾经在 C++ 开发中遇到的问题。当然,Objective-C 一定也有它自己的坑,只是我才刚开始,没有踩到过罢了。


ObjC 的类方法调用的形式,更接近于向对象发送消息。语法写作:

[obj message]

如果方法带有参数,那么就写作

[obj param:value]

方法和名称和参数的名称是一体的,参数决定了方法是什么。如果有多个参数,那么写作:

[obj param1:value1 param2:value2]

注意,如果一个类有两个方法,一个有一个参数,一个有两个参数。即使两个参数的版本中有一个参数名称和单个参数版本的相同,它们也是两个不同的方法。ObjC 不支持默认参数的语法。

C++ 调用对象的方法就更接近于 C 的函数调用。两相比较,可以发现 ObjC 的语法让代码可读性更强。你可以很容易的理解参数的用途,也不怕方法参数过多时,一串参数写漏或写错次序了。


和 C++ 一样,ObjC 的类声明和实现是分离的。但做的比 C++ 更彻底。ObjC 不能在声明的代码段中写 inline 函数。这看起来牺牲了一些运行性能,但当实现部分更好的分离。作为补充,ObjC 有 @property ,可以帮助程序员简化实现,也可以让编译器生成更好的代码。

声明一个类写成这样:

@interface class : baseclass {
   type a;
}

- (void) method;

- (void) messge: (type) param;

+ (id) create ;

@end

ObjC 利用了 C 语言中没有使用的符号 @ 来扩展 C 的语法,而不是用 C++ 里增加关键字的方式。这或许是一个对语言扩展更简单的做法,而不用考虑兼容性。C++ 就得精心挑选新增加的关键字,尽量回避那些已有代码中高频出现的单词。

类的数据段和方法是分离的。数据描述放在 {} 中,方法写在其后,在 @end 之前。

"-" 开头的方法是实例方法,也就是 C++ 中的成员方法。成员方法中可以通过 self 取到实例指针,也就是 C++ 中的 this 指针。

同样,ObjC 也支持类方法,也就是 C++ 中的 static 成员方法。通常是用来构造实例。声明方法是在方法名前写一个 + 号。

和 C++ 不同,ObjC 是有类对象的。类对象里有超类指针、类名、类方法列表指针,还有类对象的字节大小等元信息。而 C++ 中是用 RTTI 类实现不完全的类似功能的。

调用类方法和调用实例方法在语法上没有什么不同。类名就是类对象的名字。

ObjC 不支持多继承,没有私有、公开这些修饰符。

ObjC 的类方法实现必须写在同一个源文件里。不像 C++ 有 :: 操作符,ObjC 在实现方法时不写类的名字,而是把所有实现都写在 @implementation class ... @end 之间。访问基类,也可以方便的使用 super 关键字。

那么,如果一个类的方法太多,不适合写在同一个源文件中怎么办?


ObjC 提供了 category 这个概念。

可以通过 category 为一个类添加一些方法。category 和继承是不同的,不能为类添加新的成员变量,所以它不会改变类对象的内存布局。添加了方法的类还是原来那个类。

category 的语法是这样的:

@interface class (category) 

- newmethod;

@end

这样,就给 class 类添加了一个方法 newmethod ,并归类在 category 下。

和 C++ 不同,ObjC 的方法更具动态性。你可以在运行时任意调用一个对象的方法,而不用管它是否存在。ObjC 支持 id 这个类型。 id 其实就是对象指针,任何类型的对象都可以被 id 引用,并可以方便的向其发送消息(方法调用)。如果方法不存在,会抛出运行时错误。

向一个指定类型发送一个不存在的消息,会得到一个编译期警告,而不是编译错误。当然,我们不能随便忽略编译期警告,如果我们清楚的知道运行期这个对象可以处理这个消息,那么可以给类加一个 category 但不必实现它。这样,编译器就能了解新的方法了。

利用 category 可以方便的一个庞大的类拆分成独立的模块。在 C++ 中,比较接近的概念是 friend ,不过 friend 不易被优雅的使用。

既然方法可以被运行期检查,那么方法本身在 ObjC 中也可以被当成一种类型来处理。比较接近的 C++ 中的概念是 成员方法指针。回顾学习 C++ 的经历就能回忆起当年使用 ::* 或是 ->* 的头痛经历。ObjC 中的方法可以运行期绑定, @selector(method:) 的语法也简单的多。

在 NSObject 中就提供了一个叫 respondsToSelector: 的方法,接受一个 selector 用来检查自己是否可以接受这个消息。


ObjC 也提供了类似 Java 的 interface 或是 C++ 的纯虚类的东西,在 ObjC 中被称为 @protocol 。

@protocol 可以看成是一种没有数据成员的虚类。一个实际的类可以声明自己实现了某些协议,语法是

@interface class : base <protocol>
{
   // variables
}

// methods
@end

和继承不同,一个类可以声明多个协议。然后在 @implementation 中必须一一实现它们。

如上所述,ObjC 已经做到了运行期的方法绑定,所以 @protocol 只是做了更严格的编译检查。在新版的 ObjC 2.0 中,追加了 @optional 和 @required 用来描述那些方法的实现是可选的,哪些必须实现。


ObjC 的基础库比 C++ 更完整,标准化要好的多,也和语言结合的更紧密。

比如 NSString 是一个基础类,用于处理字符串。同时,语言也提供 @"string" 的语法方便的生成 NSString 对象。

ObjC 保留了 C 中的 printf 式的字符串操作形式,对比 C++ 重载移位操作符的形式,我想要更清爽一些。

对于 ObjC 对象,使用 %@ 来表示。给对象增加 description 方法就可以让处理函数知道该如何处理这个对象的 %@ 行为。

Comments

我可以这么理解吗?
在C++上面栽跟头的程序都来OC来寻找成就感了,别谈什么内存管理autorelease,如果在C、C++里面delete这个都弄不会,那也就别来对比两者了,毕竟连C的门槛都没入。
乔布斯偏执大家有目共睹,他成就了iOS这优秀的系统(Mac系统还是算了),但系统并不是OC的功劳,而是C++、C架构的,APP部分才是OC。
如果不是苹果的封闭性,如果平台上面能够使用C++等语言开发,估计OC早就被抛弃到爪哇国了。
更别谈什么优雅这个词,一门开发语言使用「优雅」这种无法量化的形容词,是其它方面都比不过了么?。最后蘋果还不是发明了Swift语言,更接近Simula,毕竟全世界都是使用的Simula风格。
培训学校是看什么好赚钱培训什么,早年培训.Net,而今培训iOS、Java。如今iOS程序员已经烂大街了(就业环境逐渐稳定,普通小公司一天5个iOS面试没问题)
这个语言(语法)太小众,除非iOS、Mac常青,那大家还是趁早去学习Simula风格吧,至少在未来几十年不会落伍。

非常感谢,言简意赅

不得不学啊,不过确实是反人类。

云风大哥对新出的swift有什么感觉么?

既然obj-c给你的感觉不错,更清爽,为什么不见有人,包括你用obj-c写普通的服务端程序,而不仅仅限制在macos、ios里呢?

至少我看着一堆堆的方括号好恶心。

@private表示私有
不过gcd,block设计才是经典
方法的灵活性也将导致安全性的降低,按照win的说法,满大街都可以hook

反正都是中缀表达式的渣渣,语法就懒得吐槽了。S-expr以下内建扩展语法?省省吧。
要说引用计数就别扯GC了,不是一个层次上的东西。GC只不过是管理内存这种资源的特定优化罢了;引用计数作用的对象范围更广。
cocos2d-x这货……烂到能让人看了个CCPoint就没兴趣用的程度……居然还能在这种基本类型的布局上出bug。。。

一直学不会C++,但很快学会了OC,我觉得OC很优雅
除了方法会长点,其他都很好

个人认为oc中最cool的是两个地方:
1. 内存管理
统一了内存管理模型,使用引用计数。只要你的类继承自NSObject,即具备了引用计数功能,有retain和release方法。ios5之前需要开发者手工释放引用计数[yourobject release],要理解autorelease等概念,这个有一定负担且稍不注意可能造成内存泄露。在加入ARC机制之后,[yourobject release]这种代码都省去了,c++转oc的孩纸们偷着乐吧。当然坑还会有,例如可能出现retain cycle,但这种坑已经很少了,且xcode、llvm的演化速度感觉还是很快的,估计在几个版本内都可以在编译器层面解决这种坑。当然,能够自动帮你管理内存的语言有很多,但一般都是gc实现的,对性能似乎或多或少都会有影响。而OC的ARC机制,是编译器在合适的地方帮你插入类似[yourobject release]的代码,效率无损失。

2. 异步处理
GCD。这是另外一个屌爆了的机制。对比C++多线程开发的痛苦(看chrome花了多大的精力用C++搭了一套符合他们需求的线程模型就明白了),OC提供的是OS、语言和库等的支持,用这个利器开发多线程,爽到爆。具体原理和使用不展开写了,有兴趣的先自己google下吧。

oc的的声明和实现是可以写在多个文件的,不一定非要在一个文件

C++的好或者坏,没有任何人能有资格去鉴定,有句话相信没人会反对,这世界上本来就没有绝对完美的东西

c++的坑太多,可又不得不学啊。有机会我也要学Objective-C

OC比C++容易上手多了,而且坑并不多,目前只有引用计数这个坑比较大...常常会因为引用计数崩溃..
至于C++.我觉得,这个语言太烂了....为了种树制造了种树的铁锹,为了挖沟制造了挖沟的铁锹.为了挖战壕,制造了挖战壕的铁锹,真牛B...
而C,就一把铁锹....

云风为了黑C++无所不用其极啊

学习和使用OC2年多,个人还是很喜欢的!~
另外:“ObjC 的类方法实现必须写在同一个源文件里。不像 C++ 有 :: 操作符,ObjC 在实现方法时不写类的名字,而是把所有实现都写在 @implementation class ... @end 之间。访问基类,也可以方便的使用 super 关键字。

那么,如果一个类的方法太多,不适合写在同一个源文件中怎么办?”
这里,我觉得category可以写到多个.m文件里的。

引用计数器方面,编译型语言也有,比如delphi,虽然不如oc早,但是内部的许多变量(string,返回值直接返回结构而不是指针.编译器会自己动态申请内存,不用的时候自动释放)都是编译器自动生成和连接的,最后函数结束的时候自动释放。而且效率很高。delphi可以名噪一时也和这个有很大关系。可惜anders后来去微软了,不然不知道会带来什么新的功能

@吕子熏 OC一直都是iOS和MacOS的专用开发语言,至少目前没打算去其他系统,再说有更好的IDE不是更加可以加快开发效率么?非要用vi,emacs这样的东西写代码才叫写代码?xcode很好用而且效率很高。写c的时候我才会用vi or emacs,C++稍大一点的工程这两个神器就扛不住了,想想怎么加快开发效率吧,有的时候我就在windows下写代码,linux下编译调试。有简单的方法非要用复杂的想不通。

为什么都说oc依赖NS呢?
gcc+libobjc本身就不需要ns啊,还有庞大的c库可以封装复用。

关注3年,终于完全看懂一片,内牛满面!

我之前也是从C++过渡到Obj-C, 对于内存管理来说,一旦掌握了那基本的几条规则,使用起来也很顺手,尤其是autorelease的使用,可以简化不少代码,而且现在出了ARC以后,更简单了,跟GC差不多
category现在也可加instance变量了,非常方便。

@omega 引用计数确实不是oc固有的,无非是gc的一种工作方式。 关键的问题是,这种东西要么全权抛给程序员自己管理象标准的malloc 和 free,要么就把复杂性屏蔽掉。让使用者认为引用计数只是其gc的一个工作方式。但oc却是把这个东西一半抛给程序员,一半给编译器。就算这样能忍了,那就把规则弄简单些。但是,他偏把引用计数加减的规则搞的那么复杂。定义个标识符都有可能触发引用增加。这是什么道理? 自己家的语言跟NS库绑定的那么紧,我都在想有人能脱离了Xcode (刷新全球IDE下限),能写的出来Oc代码吗?

看到一篇自己懂一些的文章和评论,想写点东西。

@paladin_t cocos2d-x这样移植的初衷是可以让原cocos2d-iOS的代码方便的移植,我认为这样做有利于它生存下来。自动内存管理移植的并不生硬,OC是在每次运行循环结束后做AutoReleasePool的管理;而cocos2d-x模拟了这一过程,在游戏引擎的很次绘制循环结束后做AutoReleasePool管理,所以使用效果上几乎没有差别。

@吕子熏 引用计数不是OC的专利,这样吐槽不科学哦 xD。

真心无意针对两位,如果喷请轻喷。

斗胆推荐一系列文章给云风,OC底层一点的很有意思的解析:
http://www.cnblogs.com/studentdeng/archive/2011/10/06/2199873.html

class没有保护方法,离了NS库之后,啥都干不成。 而且语言中夹带的私货很多,什么是叫以init,alloc 开头命名的函数将会引用计数加1? @property定义的属性a和_a,又是啥关系?oc里面的闭包被称为block,在block里面访问外层变量,尼玛还要block_copy。我是无法理解为什么一个函数定义时有多个参数还要对每个参数写一遍名字。oc脱了xcode,没个自动补全和查找根本就不是人类所能写的出来的。

开始用的时候,因为那个引用计数而产生的内存泄露多了去了,这货就是一反人类,ios黄了的话,立马没人用这玩意儿了!

“ObjC 不支持多继承,没有私有、公开这些修饰符。”

貌似是有私有、公开修饰符的,只是一般利用写在h或者写在m文件中来区别。

最近看cocos2d-x,代码确实非常糟糕。完全照搬oc中的东西,实在是丑陋。。

运行期方法绑定是指可以随便调用某个对象的某个方法,但这个方法其实你并没有定义过,编译也不会出错?怎么这么像动态语言。

我以前还以为-表示private,+表示public呢。 是不分private和public?

ObjC确实比C++容易上手太多,期待云风大 更多内存管理方面的介绍~~

准备出手游了?

忍不住要吐槽一下cocos2d-x的实现,从Obj-C到C++移植逻辑之余将Obj-C的protocal,selector,delegate,自动内存管理等也生搬过来了,极其难用,不如常规的C++做法舒服。看来有些事不从语言底层做起是不行的。

开头的方法是实例方法,也就是 C++ 中的成员方法。

这句话前面应该是-号吧

Objective-C可以直接简单的看成是C和Smalltalk的合体,甚至将代码块这种能够拓展函数式编程语法的东西都直接给导入进去,这样子的做法能在语言能力上拓宽很多,比起C++要通过模板元编程来扩展表现的有远见了许多。

但是缺点是不是也很重,就是必须要依赖NextStep的基础库,而不是成为一个比较通用的编程语言。如果社区有扩展让他可以不止局限于NS基础库的实现平台的话,可能会更受欢迎一点吧。

看口气 云风大大 把objective-c夸得不轻啊
哈哈

难得有时间学习

我也在学

沙发沙发
太难得了

Post a comment

非这个主题相关的留言请到:留言本