Objective-C和C#有什么区别?

88

我最近购买了一台Mac,并主要在VMWare Fusion下进行C#开发。有了所有漂亮的Mac应用程序,我开始考虑学习Objective-C,只需点击安装即可。

两种语言之间的语法看起来非常不同,这可能是因为Objective-C的起源是C,而C#的起源则是Java/C++。但是不同的语法可以学习,所以应该没问题。

我的主要关注点是使用该语言是否有助于生成结构良好、易读且优雅的代码。我非常喜欢C#中的特性,如LINQ和var,并想知道在Objective-C中是否有等效或更好/不同的特性。

在使用Objective-C开发时,我会错过哪些语言特性?我会获得哪些特性?

编辑:框架比较很有用和有趣,但这个问题实际上是在问语言比较(部分是我最初标记为.net的错误)。推测Cocoa和.NET都是非常丰富的框架,各自都有自己的目的,一个面向Mac OS X,另一个面向Windows。

谢谢到目前为止思考得很好并且相对平衡的观点!


27
Xcode并不完美,但它确实具有许多强大而有用的功能。如果没有支持或提供任何比较背景的情况下把某物贬低为“纯邪恶”,这种FUD并没有帮助任何人。无论如何,这与问题无关。 - Quinn Taylor
11
Xcode 3.2可能是我使用过的最好的集成开发环境,它具有优雅的断点处理、静态分析、内联错误信息和大量其他功能。顺便说一句,“Xcode”而不是“XCode”。@Nathan,我真的不明白为什么你需要在关于编程语言的讨论中毫无理由地诋毁Xcode,称其为“纯邪恶”。 - Debajit
6
使用Objective-C的一个重要好处是可以访问Cocoa框架。另外,C、C#、Java和C++都有共同的语法渊源,而Objective-C的语法则来自Smalltalk。 - Amok
4
你是使用VMWare Fusion进行C#开发的吗?只需使用Mono和MonoDevelop(它是适用于Mac和Linux的.NET和Visual Studio的开源版本)。使用Mono,你还可以创建使用C#编写的Mac和iPhone应用程序,并同时使用.NET库和Cocoa。请注意,这并不会改变原始含义。 - Robin Heggelund Hansen
5
@user668039有15年使用C#的经验?那可不得了!因为C#是在2001年发布的。 - Ian Newson
显示剩余7条评论
11个回答

89

没有一种语言适用于所有任务,Objective-C也不例外,但有些非常特定的优点。比如使用LINQvar(我不知道有什么直接替代品),其中一些是严格与语言相关的,而另一些则与框架相关。

(注: 正如C#与.NET紧密耦合一样,Objective-C与Cocoa紧密耦合。因此,我的一些观点可能与Objective-C无关,但没有Cocoa的Objective-C就像没有.NET / WPF / LINQ在Mono下运行的C#一样,这不是通常的做法。)

我不会详细阐述差异、优缺点,但以下是我想到的一些。

  • Objective-C最好的部分之一是动态性——你通过发送消息而不是调用方法,运行时动态路由。与动态类型结合使用(谨慎地),这可以使许多强大的模式更简单甚至是微不足道的实现。

  • 作为C的严格超集,Objective-C相信你知道自己在做什么。与像C#和Java这样的托管和/或类型安全语言相比,Objective-C让你想干什么就干什么,并体验后果。显然,有时这可能是危险的,但语言不会积极防止你做大多数事情的事实相当强大。(编辑: 我应该澄清一下,C#也有“不安全”的功能和功能,但默认行为是托管代码,你必须明确选择退出。相比之下,Java只允许类型安全代码,并且从未像C和其他语言那样公开裸指针。)

  • 范畴(在没有子类化或无法访问源代码的情况下添加/修改类的方法)是一个很棒的双刃剑。它可以极大地简化继承层次结构并消除代码,但如果你做了奇怪的事情,结果有时会令人迷惑。

  • Cocoa使创建GUI应用程序变得更加简单,但你需要理解其编程范式。MVC设计在Cocoa中普遍存在,并且诸如委托、通知和多线程GUI应用程序等模式非常适用于Objective-C。

  • Cocoa绑定和键值观察可以消除大量粘合代码,而Cocoa框架广泛利用了这一点。Objective-C的动态分派与此相辅相成,因此只要对象符合键值规范,其类型就无关紧要。

  • 你可能会错过泛型和命名空间,它们也有其好处,但在Objective-C的思维方式和范式下,它们将是额外的奢侈品而不是必须品。(泛型主要是关于类型安全和避免强制转换,但Objective-C的动态类型使其基本上不是问题。如果命名空间做得好,它们将是很好的,但避免冲突很简单,成本很可能超过收益,特别是对于旧代码来说。)

  • 对于并发,Blocks(Snow Leopard中的新语言特性,在许多CocoaAPI中实现)非常有用。几行代码(通常与Grand Central Dispatch配合使用,后者是10.6中的libsystem的一部分)可以消除回调函数、上下文等重复代码。(Blocks也可以用于C和C++,并且肯定可以添加到C#中,这将非常棒。)NSOperationQueue也是一种非常方便的方法,通过将自定义的NSOperation子类或匿名块分派给GCD自动在一个或多个不同线程上执行。


  • 7
    从技术上讲,C# 拥有所有与 ObjC 同样不安全类型的强大功能:原始指针、指针算术、边界未检查的数组、联合体和 alloca。只是它没有让这些功能很容易地被访问到 - 你需要显式地选择使用它们。 - Pavel Minaev
    8
    我提到Cocoa只是因为在Objective-C编程中吸引人的大部分原因都归功于Cocoa框架。如果没有.NET和/或WPF,C#会是一个有趣的选择吗?问者特别提到了LINQ,显然他在寻求超越语言本身的体验。 - Quinn Taylor
    7
    没问题,我不想跟C#过不去。如果我必须写Windows软件,那就是我要用的语言。我只是试图指出这些语言和相关工具之间的相似之处和差异。你显然比我更有C#方面的经验,而我则更熟悉Objective-C。我感谢你的澄清,但也许更具建设性的回答和少一点的纠缠细节会更有用。 - Quinn Taylor
    11
    这不是吹毛求疵,Quinn。我只是指出你在回复中把最开始的语言比较转变成了对Cocoa好处的讨论。我仍然想指出,你对Cocoa的观点并不是“比较”——它们说“它做X很好”——这可能是事实——但它们没有说“它做X比C#+WPF更好/更差”,而这正是问题的核心。 - Pavel Minaev
    6
    +1 良好回答,Quinn。 - h4xxr
    显示剩余6条评论

    56

    我已经用C、C++和C#编程20多年了,最初是在1990年开始的。现在我决定看看iPhone开发以及Xcode和Objective-C。天啊……我所有对Microsoft的抱怨都收回了,我现在意识到事情可能有多糟糕。相比于C#,Objective-C过于复杂。我已经被C#宠坏了,现在我很欣赏微软所做的努力。只是阅读带有方法调用的Objective-C就很难懂了。C#在这方面非常优雅。那只是我的看法,我希望苹果的开发语言和产品一样好,但亲爱的,他们有很多东西需要向微软学习。没有什么.NET应用程序可以让我比使用XCode Objective-C更快地完成应用程序的构建。苹果确实应该向微软学习,然后我们就会拥有完美的环境了。 :-)


    47
    即使您浏览了 iPhone 开发书,但在您具有 20 年经验的平台上创建应用程序速度依然更快?太棒了! - kubi
    25
    我有和Waz完全相同的经历。在我开始学习Objective C之后,我也更加感激Microsoft为C#以及开发工具如Visual Studio所做的工作。 - René
    5
    在学习Objective-C时,我的经历也是如此,与C#相比,它过于复杂。@alexy13 你错过了原始留言中的C和C++哪一部分?对于同一语系的其他编程语言,拥有数十年的经验会非常有帮助,可以更好地评估使用另一种语言完成相同工作的难度。 - Anderson Fortaleza
    5
    这个最佳答案怎么样?它不是一个简洁的答案,只是某个人对他一夜之间学习的语言和他使用二十多年的语言明显带有偏见的观点...显然你在新语言上不会像老语言那么熟练。 - Heavy_Bullets
    3
    仅仅阅读Objective-C的方法调用就很难阅读,这是提到Obj-C缺点的一个非常主观的论点。其他所有内容都只是有争议的修辞。 - skywinder
    显示剩余5条评论

    47

    这里没有技术审查,但我发现Objective-C的可读性要低得多。 根据Cinder6给出的例子:

    C#

    List<string> strings = new List<string>();
    strings.Add("xyzzy");                  // takes only strings
    strings.Add(15);                       // compiler error
    string x = strings[0];                 // guaranteed to be a string
    strings.RemoveAt(0);                   // or non-existant (yielding an exception)
    

    Objective-C

    NSMutableArray *strings = [NSMutableArray array];
    [strings addObject:@"xyzzy"];
    [strings addObject:@15];
    NSString *x = strings[0];
    [strings removeObjectAtIndex:0];
    

    看起来很糟糕。我甚至尝试阅读了两本书,但很快就迷失了方向,而通常我在学习程序语言/编程书籍时不会遇到这种情况。

    我很高兴我们有了适用于Mac OS的Mono,因为如果我必须依赖苹果提供给我好的开发环境......


    17
    忽略第一行应该以[NSMutableArray array]结束(他在泄漏内存),第四行应该以NSString* xid x开头(编译错误)...是的,Objective-C缺乏泛型(我真的不太需要它们),你不能使用数组语法对对象进行索引,并且API通常更冗长。就是“土豆、西红柿”。这实际上取决于你更喜欢看到什么。(你可以用C++ STL编写相同的代码,但我会觉得它很丑陋。)对我来说,可读性良好的代码通常意味着代码文本告诉你它正在做什么,因此Objective-C代码更可取。 - Quinn Taylor
    18
    我认为语法本身并没有什么问题——让人感觉不易读的主要原因是:1)对于所有来自C语言背景的人来说,它都很陌生;2)它被用在普通C语句的中间,看起来很奇怪。另一方面,Smalltalk风格的将命名参数作为方法名的一部分实际上使得调用更容易理解。事实上,你的C#代码示例很好地说明了这一点——它无法编译,因为List<T>.Remove需要一个字符串作为参数,并删除其中第一个出现的字符串。要按索引删除,你需要使用RemoveAt - Pavel Minaev
    12
    使用removeObjectAtIndex:的ObjC版本虽然冗长,但其作用是明确无歧义的。 - Pavel Minaev
    6
    很好的观点,Pavel。此外,strings[0]strings.RemoveAt(0)之间的不一致性会降低清晰度,至少对我来说是这样。另外,当方法名称以大写字母开头时,我总是感到很烦...... 我知道这只是个小问题,但它很奇怪。 - Quinn Taylor
    4
    对于我和许多人而言,“工具”的易读性会影响我的生产力。我擅长使用许多编程语言,但 Objective-C 却不是其中之一,这并不是因为我尝试过了。但最终,这完全取决于个人偏好。与 20 年前不同的是,你现在不必强制使用语言 X 来针对平台 Y。你可以选择最适合自己的语言。 - TimothyP
    显示剩余10条评论

    17

    手动内存管理是Objective-C初学者经常遇到的问题,主要是因为他们认为这比实际复杂得多。

    Objective-C和Cocoa通过约定而非强制来实现内存管理;只需知道并遵循一小组规则,你就可以通过运行时获得许多免费服务。

    虽然不是100%正确的规则,但对于日常工作来说足够好:

    • 每次调用alloc都应该在当前范围结束时与一个release匹配。
    • 如果你的方法返回值是通过alloc获得的,则应该通过return [value autorelease];返回它而不是通过release匹配它。
    • 使用属性,没有第三个规则。

    下面是更详细的说明。

    内存管理基于所有权;只有对象实例的所有者才能释放对象,其他人永远不应该释放。这意味着,在95%的代码中,你将Objective-C视为具有垃圾收集功能。

    那么另外的5%呢?你需要注意三种方法,从中接收到的任何对象实例都由当前的方法范围拥有:

    • alloc
    • 任何以单词new开头的方法,例如newnewService
    • 包含单词copy的任何方法,例如copymutableCopy

    在退出方法之前,该方法必须对拥有的对象实例执行以下三种可能的操作之一:

    • 如果不再需要,使用release释放它。
    • 将所有权移交给一个字段(实例变量)或全局变量,只需进行简单的赋值操作即可。
    • 放弃所有权,但在实例消失之前让其他人有机会拥有它,只需调用autorelease

    那么,何时应该通过调用retain主动承担所有权呢?有两种情况:

    • 在初始化器中分配字段时。
    • 在手动实现 setter 方法时。

    2
    +1 优秀的总结。听起来和我多年前学习 C 时很相似。 - Alex Angas
    4
    只是为了澄清,垃圾收集在 Mac 上对 Objective-C 的支持是完整的,无需进行任何手动内存管理。手动内存管理仅在 iOS 中需要。 - Debajit
    3
    学习内存管理的基础知识总是有好处的,即使只是为了在需要时提高性能(无需运行垃圾回收)。 - FeifanZ
    3
    iOS 5引入了ARC,无需手动释放已分配的对象。但它仍然不像C#那样容易。你必须记住Objective-C已经超过20年了,而且它保留了一些早期语言的要求:即知道何时分配和释放内存。C#已经为您删除了所有这些内容。话虽如此,Cocoa拥有许多API在Windows Phone上尚未像手势、更多的UI事件控件等方面那么易用。 - jyavenard

    11

    如果你的生活中只看到过Objective C,那么它的语法看起来可能是唯一的选择。我们可以称你为“编程处女”。

    但是由于很多代码都是用C、C++、Java、JavaScript、Pascal和其他语言编写的,你会发现ObjectiveC与它们都不同,但这并不是一件好事。他们为什么要这样做呢?让我们看看其他流行的语言:

    C++在增加了很多额外功能的同时,只在必要的情况下改变了原始语法。

    C#相对于C++添加了很多额外功能,但只改变了C++中丑陋的部分(比如从接口中删除“::”)。

    Java改变了很多东西,但保留了熟悉的语法,除非需要进行更改。

    JavaScript是一种完全动态的语言,可以做许多ObjectiveC无法做到的事情。尽管如此,它的创建者也没有发明一种新的方法来调用方法和传递参数,只是为了与世界其他地方不同。

    Visual Basic可以像ObjectiveC一样无序传递参数。你可以命名参数,但也可以按常规方式传递它们。无论你使用哪种方式,都是人人都理解的普通逗号分隔方式。逗号不仅是编程语言中常用的分隔符,也是书籍、报纸和一般书面语言中常用的分隔符。

    Object Pascal的语法与C不同,但它的语法对于程序员来说实际上更容易阅读(也许不适用于计算机,但谁在乎计算机的想法)。所以也许他们偏离了主题,但至少他们的结果更好了。

    Python有不同的语法,甚至比Pascal更容易阅读(对人类来说)。因此,当他们进行更改时,至少他们使其更好,而不只是为了与众不同。

    然后我们有ObjectiveC。它增加了一些对C的改进,但发明了自己的接口语法、方法调用、参数传递等。我想知道为什么他们没有交换+和-,这样加号就可以减去两个数。这会更酷一些。

    史蒂夫·乔布斯犯了一个错误,他支持了ObjectiveC。当然,他不能支持更优秀的C#,因为它属于他最糟糕的竞争对手。所以这是一个政治决定,而不是实际的决定。当技术决策出于政治原因时,技术总是会遭受损失。他应该领导公司,这方面他做得很好,把编程事务留给真正的专家。

    如果他决定使用任何其他语言编写iOS并支持库文件,除了忠实的粉丝、新手程序员和史蒂夫·乔布斯外,我相信iPhone上会有更多的应用程序。对于除了忠实的粉丝、新手程序员和史蒂夫·乔布斯之外的所有人来说,ObjectiveC看起来荒谬、丑陋和令人反感。


    3
    最后两段概括了所有内容。 - Zakos
    哦,是的,语法看起来很荒谬,但 Objective-C 语言的精华在于它几乎拥有所有编程语言中最易于使用的反射功能和一些非常出色的运行时特性,使得实现几种范式几乎变得轻而易举。例如,在使用 Objective-C 时,我永远不需要关心要使用哪个线性表 - 链表还是向量或其他什么 - 只需使用 NSArray,它会根据机器和数据的性质选择最佳算法。 - Maxthon Chan
    我作为一名(处女)程序员进入了我的第一份工作,实现了Objective-C中的iOS应用程序用户界面。现在(三年后),我想要的就是离开那个孤独的“岛屿”,学习更多平台无关的东西,因此更具有创新性。同时,我也想了解其他框架是否也会更新得如此迅速 - 以及存在的漏洞。嘿!新功能!- 崩溃...希望(德国)职业中心能够为我花费一些钱来学习Java和/或C++/C#的教程,因为我不再想为苹果的破玩意开发。 - anneblue

    5

    我喜欢Objective-C的一件事情是它的对象系统基于消息,这使你能够做很棒的事情,在C#中无法实现(至少在支持动态关键字之前不能实现)。

    另一个编写Cocoa应用程序的好处是Interface Builder,它比Visual Studio中的表单设计器要好得多。

    作为C#开发人员,让我感到困扰的是你必须管理自己的内存(虽然有垃圾回收,但它在iPhone上不起作用),并且由于选择器语法和所有[ ],代码可能非常冗长。


    2
    “dynamic”只能让你走一半路——即使在C# 4.0中实现真正的动态对象,甚至是像消息委托这样的琐碎事情,也是很繁琐的。另一方面,采用ObjC风格的真正动态消息传递的灵活性的代价是失去类型安全性。 - Pavel Minaev
    3
    值得一提的是,Objective-C允许选择静态类型,这确实提供了更高程度的类型安全性。有些人更喜欢编译时检查,而另一些人则更喜欢运行时检查。在这两种语言中,好的一点是,选择并不需要绝对。 - Quinn Taylor
    3
    Windows Forms 设计器不是很好,但如果你能够使用 WPF(自 .NET 3.0 起),Expression Blend 就难以被超越了。 - Nate
    你可以使用C# 3.0中的System.Reflection结合扩展来完成很多操作,你可以调用任何你找到的方法,但这并不是真正的消息传递,它看起来像是obj.PassMessage("function_name", params object[] args);。为了更好地将消息传递集成到语言中,需要在普通对象上调用缺失的方法。 - Filip Kunc

    4
    作为一个刚开始学习Objective-C的iPhone程序员,来自C# 4.0,我想念lambda表达式,特别是Linq-to-XML。Lambda表达式是C#特有的,而Linq-to-XML实际上更多地是.NET与Cocoa之间的对比。在我编写的一个示例应用程序中,我有一些XML字符串。我想将该XML的元素解析为对象集合。
    为了在Objective-C/Cocoa中实现这个,我必须使用NSXmlParser类。这个类依赖于另一个实现了NSXMLParserDelegate协议的对象,该协议包含一些方法,在读取元素开标签时调用(即发送消息),在读取一些数据时调用(通常在元素内部),以及在读取一些元素结束标签时调用。您必须跟踪解析状态和状态。老实说,如果XML无效,我真的不知道会发生什么。这对于深入了解细节并优化性能非常有用,但是代码量太多了。
    相比之下,这是C#中的代码:
    using System.Linq.Xml;
    XDocument doc = XDocument.Load(xmlString);
    IEnumerable<MyCustomObject> objects = doc.Descendants().Select(
             d => new MyCustomObject{ Name = d.Value});
    

    这就是全部,你已经得到了从XML中提取的自定义对象集合。如果您想按值过滤这些元素,或仅限于包含特定属性的元素,或者只想获取前5个元素,或跳过第一个元素并获取接下来的3个元素,或只是查找是否返回任何元素... BAM,所有这些都在同一行代码中。在Objective-C中有许多开源类可以使此处理变得更加容易,因此完成了大部分繁重的工作。它只是没有内置在其中。
    *注意:我实际上没有编译上面的代码,它只是作为一个例子,以说明C#所需的相对缺乏冗长性。

    需要注意的是,在这种比较中,C# 并不是缺乏冗长性,而是你使用的 .net 框架类用于解析 XML 中包含了冗长性。如果你检查其中的代码,很可能会发现更多的 C# 代码,甚至更加冗长。 - XIVSolutions

    3

    最重要的区别可能是内存管理。由于C#是基于CLR的语言,因此您可以获得垃圾回收功能。而在Objective-C中,您需要自己管理内存。

    如果您来自C#背景(或任何现代语言),转换到一种没有自动内存管理的语言将非常痛苦,因为您将花费大量编码时间来正确地管理内存(以及调试)。


    6
    现如今,ObjC拥有追踪垃圾回收功能。而另一方面,C#允许您自行管理内存(感谢原始指针)。 - Pavel Minaev
    3
    在苹果框架的上下文中,我认为Objective-C中的手动内存管理并不太麻烦:保留/释放/自动释放池循环比C和C ++中的“传统”内存管理要简单得多。 - mipadi
    5
    这只是不准确的说法DSO - 即使在像iPhone这样没有垃圾回收机制的环境中,保留/释放和自动释放等内容也只需要花费大约10分钟来学习,然后就不再是问题了。我发现许多C#程序员夸大了内存管理的挑战。这几乎就像他们害怕否则会开始太喜欢Obj-C一样... ;) - h4xxr
    4
    手动内存管理的基础并不难。但当你开始传递已分配对象并需要处理复杂对象图时,情况会变得非常复杂,因为你需要仔细遵循规则,了解谁有责任释放以及何时释放。引用计数实现使这变得更加容易,但你需要处理对象图中的环路......与C#相比,我认为这是一个很大的区别。 - dso

    1

    很遗憾,该页面声称使用UTF-8编码,但实际上却使用了各种令人讨厌的撇号和引号替代符号。我认为他的文本使用了“弯曲”的引号,但它们确实破坏了代码... - Quinn Taylor
    他们似乎完全抄袭了引言,但即使是源文章也对他们的代码进行了修改。两篇文章都不是特别好的文章。 - dnord

    -2

    除了这两种语言之间的范式差异外,它们之间没有太大的区别。虽然我不想这么说,但你可以用.NET和C#做与Objective-C和Cocoa相同类型的事情(可能不那么容易)。自Leopard以来,Objective-C 2.0具有垃圾回收功能,因此您不必自己管理内存,除非您想要(与旧版Mac和iPhone应用程序的代码兼容性是想要的两个原因)。

    就结构化、可读性强的代码而言,其中很大一部分负担在程序员身上,就像任何其他语言一样。然而,我发现消息传递范式很适合编写可读性强的代码,只要您适当地命名函数/方法(再次强调,就像任何其他语言一样)。

    我首先承认我对C#或.NET不是很熟悉。但Quinn列出的原因是我不想成为专家的几个原因。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接