Objective-C 2.0从2006年正式发布至今已经有10年了。Apple在此期间也不断地为其注入新的语法特性,比如Blocks、NSNumber literal、NSArray literal、NSDictionary literal、@() compund literal、Object subscripting、instancetype、lightweight generics等等。然而,其核心语法变化不大。

本人从2009年夏季开始接触Objective-C,一开始总是不习惯其[object   message]这种语法形式,不过随着Xcode自身智能感知的不断加强,编辑也逐步方便,所以也渐渐地习惯了,呵呵~现在用下来,感觉Objective-C在面向对象编程方面有其独到之处,而且灵活性相当大,编译器负担也很小,增加的仅仅是运行时库。不过相对于Java这种需要虚拟机的运行时来说要小得多。因此,它是对C++与Java的折衷。C++太过依赖编译器,Java的运行时太过庞杂,而且这两种编程语言对于类型的限制太过重,导致原本一些本应该十分灵活强大的语法特性也受到很多制约。所以,本人也时常跟朋友、同事之间开玩笑地说,“用C++来做项目,总是先考虑其各种语法糖以及各种坑,而往往不能开门见山地进行软件设计”。而Java新增的很多语法也是比较鸡肋,比如泛型就是其中之一,还有Java 8所引入的Lambda表达式。这货跟Java 1.4所引入的匿名类对象没太大不同,反而是Method Reference更有用些,呼呼~

当然,Objective-C也不能说完全没有缺陷,下面我就谈谈目前Objective-C比较令人不快的特性以及其改进建议以及新增若干语法特性。

1、消息发送机制

一开始,Brad Cox引入[object  message]的机制确实不错,这个对已有的C语言语法没有任何冲突,因此Objective-C至今对于C/C++的兼容性都能做到100%。不过,这种写法对于编写代码来说有个很大的弊端——当嵌套的方法调用过多时,由于需要往前加[,因此,对于使用不带智能感知的编辑器来说就相当讨厌了。比如:

NSString *str = [[[NSString alloc] initWithFormat:@"%d", 100] autorelease];

上述代码嵌套了多层方法调用。但是对于程序员来说,写代码时都是按照线性思维进行的。也就是说,我一开始总是会想到先分配一个NSString对象,然后调用init初始化方法对其初始化,最后想到用autorelease方法来省去后面手工release的麻烦。而用[]机制,那么你每写好一个方法调用就需要回过去加[,这显然十分麻烦~

而本人这里所提供的改进意见是,使用 . 这个操作符(operator)来表示消息发送机制:

object . message (parameter-list)

上述代码可写为:

NSString *str = NSString.alloc().initWithFormat(@"%d", ).autorelease();

整个调用过程就显得更为清晰。而之前方法的声明与定义形式依旧不变,所以,如果方法里有多于1个形参,那么后续形参也需要带有自己的标签。比如:

    NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *path = [[fileMgr URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:NULL] path];
NSLog(@"The path is: %@", path); // 可写为以下形式:
NSFileManager *fileMgr = NSFileManager.defaultManager();
NSString *path = fileMgr.URLForDirectory(NSCachesDirectory, inDomain:NSUserDomainMask, appropriateForURL:nil, create:NO, error:NULL).path();

这看上去与Swift的方法调用差不多。

那么现在估计各位可能会有这么一个问题,使用了点语法作为消息发送(方法调用)之后,如何与property加以区分呢?

如果 . 操作符后面的标识符后面没有圆括号,说明使用的是property,如果带有括号,说明使用的是方法。比如:

NSUInteger len = @"Hello".length;    // 这里使用的是property

NSUInteger len = @"Hello".length();  // 这里显式地调用property的getter方法,相当于[@"Hello" length]

此外,由于Objective-C已经有了强大灵活的消息机制,所以它不需要再包含类似于C++中指向类成员函数指针这种特性,所以用点语法其实完全能行得通,而且也能与现有的C/C++的语法完全兼容。

2、类静态成员变量与类property:

我们知道,现在Objective-C类有类方法,但是木有类属性。其实,加入类成员变量和类属性不仅能使得Objective-C在语法体系上更加完备,而且也能提供更好的模块化编程形式。

类静态成员变量非常简单,只需要在@interface或@implementation的 { }作用域中使用static存储类说明符即可。比如:

@interface MyObject : NSObject
{
@public int n; // 对象成员变量 static int s; // 类静态成员变量
} @end

对类静态成员变量的访问也是通过 -> 操作符。比如:

MyObject *obj = MyObject().alloc().init();
int a = obj->n; // 访问对象成员
a += MyObject->s; // 访问类静态成员

而要定义类property的方法也同样简单,跟定义对象property一样,只是在前面添加 + 号。比如:

@interface MyObject : NSObject
{
@public int n; // 对象成员变量 static int s; // 类静态成员变量
} - @property (nonatomic, retain) NSString *objStr; // 定义了对象property
+ @property (nonatomic, retain) NSString *classStr; // 定义了类property @end

在synthesize的时候,对象property与类property需要分开。在做类property的synthesize的时候,前面也是加 + 号进行指定。比如:

@implementation MyObject

- @synthesize objStr;      // 综合对象property
+ @synthesize classStr; // 综合类property @end

这么做可以使得对象property与类property可以有完全相同的属性名。上述代码片段中,@synthesize objStr相当于编译器给MyObject类定义了 - (void)setObjStr:(NSString*, retain)str与- (NSString*)objStr这一对setter和getter方法,同时为MyObject增添了私有对象成员变量NSString*objStr。而@synthesize classStr相当于编译器给MyObject类自动定义了+ (void)setClassStr:(NSString*, retain)str与+ (NSString*)classStr这一对setter和getter方法,同时为MyObject增添了私有类静态成员变量NSString *classStr。

这里要再提的一点是,引入了类静态property之后,实例property可以在@property前加 - 号,@synthesize之前也可加 - 号。否则对于某些星座的人来说,看代码没对齐难受得慌,呼哈哈~如果 - 缺省,那么默认为实例property。

下面我简单介绍一下类静态成员变量与类属性的实现方式。因为Objective-C完全基于C语言,可以看作是C语言的一套马甲~所以我们完全可以把Objective-C代码的编译过程理解为先将源代码完全翻译成C代码,然后再对C代码进行编译。所以,这里对于新增语法的特性也不能违背此重要原则。对于Objective-C编译器如果遇到了类静态成员以及/或类属性,那么可以把此类定义为一个全局外部对象,对象名可以诸如_objc_class_<class name>_<category name>。然后,runtime库也需要增加对类属性的访问接口。

3、带方法形式说明的SEL类型

我们在使用Objective-C时,常常会发现SEL类型参数没有具体说明,此时我们就可能需要进一步查找各种文档资料确定这个selector传多少个参数,每个参数需要是什么类型的。在Objective-C 3.0中,我们将给SEL加上方法类型说明,包括方法的返回类型以及参数类型。比如,我们常用的用于定时器消息回调的参数可以写作为:

SEL<void* (NSTimer*)>

这里之所以使用<>是因为,一来这样可以跟函数参数的圆括号进行明显区分开;二来这种表达更类似protocol,表示这个selector实参需要满足<>内的返回类型与参数列表形式。

以上就是我目前所想到的内容。各位感觉何如呢?呼呼~

漫谈Objective-C在语法上的改进的更多相关文章

  1. C++与Java语法上的不同

    最近学习算法和刷题基本都是用C++写的程序,在这个过程中,发现C++和Java在语法上有很多相同点,但也有很多不同点,而这些不同点对于已经掌握Java的程序员来说,理解C++代码可能会有些吃力甚至困难 ...

  2. 语法上的小trick

    语法上的小trick 构造函数 虽然不写构造函数也是可以的,但是可能会开翻车,所以还是写上吧.: 提供三种写法: ​ 使用的时候只用: 注意,这里的A[i]=gg(3,3,3)的"gg&qu ...

  3. swift 2.2 语法 (上)

    前言: 1.此文中的语法会根据Swift的升级变动而更新. 2.如果需要请移步 -> swift2.2 语法(中).swift 2.2语法(下) Swift与OC中常见的区别 导入框架 OC: ...

  4. 12天搞定Python,基础语法(上)

    不知你是否见过建楼房的过程,没有的话,找个时间去瞧一瞧,看一看.看过之后,你就会明白.建楼房,只有打好地基之后,才能在砌墙,建的楼层越高,打的地基就越深. 学编程也一样,要想得心应手的应用,得先打好地 ...

  5. Markdown基础语法(上)

    前言 按照官方文档,和根据自己所用和所理解所写 一.标题语法 一级标题最大,六级标题最小 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标 ...

  6. Java 和C/C++的“语法”上的差异!

    额其实认为语言语法之间是没有可比性的! 但是因为额曾经学过C/C++,而今又学Java,有赵本山说的话:“知识都学杂了!”,所以我个人总结一下,望提醒自己! Java C++ double 要用%f: ...

  7. 吾八哥学Python(三):了解Python基础语法(上)

    学习一门开发语言首先当然是要熟悉它的语法了,Python的语法还算是比较简单的,这里从基础的开始了解一下. 标识符1.第一个字符必须是字母表中字母或下划线'_'.2.标识符的其他的部分有字母.数字和下 ...

  8. 第2章 Java基本语法(上): 变量与运算符

    2-1 关键字与保留字 关键字(keyword) 保留字(reserved word) 2-2 标识符(Identifier) 案例 class Test{ public static void ma ...

  9. Java基础语法(上)

    Java编译报错出现非法字符,原因是存在中文字符. Java关键字的字母都是小写. Java是一种强类型语言,针对每一种数据都给出了明确的数据类型. 数据类型分类: A:基本数据类型 B:引用数据类型 ...

随机推荐

  1. html5+css3 background-clip 技巧

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 中国大学MOOC课程信息之数据分析可视化二

    版权声明:本文为博主原创文章,转载 请注明出处:https://blog.csdn.net/sc2079/article/details/82318571 - 写在前面 本篇博客继续对中国大学MOOC ...

  3. ggplot2入门与进阶(上)

    出处:http://www.cellyse.com/how_to_use_gggplot2_part1/ ggplot2包是基于Wilkinson在<Grammar of Graphics> ...

  4. linux实操_rpm包和yum包

    rpm包的简单查询指令: 查询已安装的rpm列表 rpm -qa | grep xxx 查询火狐浏览器 查询安装的rpm包软件的信息 查询rpm软件包的文件安装在哪里 查询文件属于哪个软件包 卸载rp ...

  5. 牛客算法:DNA序列

    import java.util.*; public class Main{ public static void main(String[] args){ try(Scanner in = new ...

  6. php 的一个异常处理程序

    <?php//exceptionHandle.php xiecongwen 20140620 //define('DEBUG',true); /** * Display all errors w ...

  7. elk with docker-compose

    version: '2' services: elasticsearch: image: docker.calix.local:18080/docker-elasticsearch:6.2.2-1 # ...

  8. hibernate步骤和配置

    1.引入hibernate的jar包和数据库驱动包 2.src添加hibernate.cfg.xml(hibernate配置文件) 3.数据库编写pojo public class Test { pu ...

  9. 【题解】狼和羊-C++

    Description 米基家的后院养着一群羊,米基由于疲劳睡着了,这时一群饿狼钻进了后院开始攻击羊群,后院是由许多个方格构成的长方形区域,每个方格中用字符'?'表示空地,'#'表示栅栏,'o'表示羊 ...

  10. Flutter布局5---Container

    Container 容器 简介一个常用的widget,它结合了常见的绘画,定位和大小调整·该容器首先让child填充绘制,然后再将其他的约束应用于填充范围.·在绘画过程中,容器先应用给定的转换,再绘制 ...