Objective-C字面量(Objective-C Literals)

翻译 2013年12月03日 13:10:53
原文地址:http://clang.llvm.org/docs/ObjectiveCLiterals.html

简介

clang同时推出了3个新的特性:数字字面量提供了从标量表达式创建NSNumber对象的语法;集合字面量提供了创建数字和字典的快速方法;对象下标提供一种Objective-C对象使用下标的途径。使用Apple编译器的用户从Apple LLVM Compiler 4.0开始可以使用这几个特性。使用开源LLVM.org发布的编译器的用户能从clang v3.1开始使用这些新特性。

这些语言特性简化了常用的Objective-C编码模式,让编码更加简洁,并且能提升装箱的安全性;

NSNumber 字面量

NSNumber这个框架类被用来把标量值装箱成对象:有符号和无符号整数(char, short, int, long, long long),浮点数(float, double), 布尔值(BOOL, C++ bool)。标量值被包装成对象也被称为装箱值。

在Objective-C中,任何的字符、数字或者布尔字面量加上'@'前缀,就可以得到指向以该字面量初始化创建的NSNumber对象的指针。C样式的后缀可以被用来控制数字字面量的尺寸(size)。

举例说明:

下面的程序阐明了NSNumber字面量的规则:
void main(int argc, const char *argv[]) {
  // character literals.
  NSNumber *theLetterZ = @'Z';          // equivalent to [NSNumber numberWithChar:'Z']

  // integral literals.
  NSNumber *fortyTwo = @42;             // equivalent to [NSNumber numberWithInt:42]
  NSNumber *fortyTwoUnsigned = @42U;    // equivalent to [NSNumber numberWithUnsignedInt:42U]
  NSNumber *fortyTwoLong = @42L;        // equivalent to [NSNumber numberWithLong:42L]
  NSNumber *fortyTwoLongLong = @42LL;   // equivalent to [NSNumber numberWithLongLong:42LL]

  // floating point literals.
  NSNumber *piFloat = @3.141592654F;    // equivalent to [NSNumber numberWithFloat:3.141592654F]
  NSNumber *piDouble = @3.1415926535;   // equivalent to [NSNumber numberWithDouble:3.1415926535]

  // BOOL literals.
  NSNumber *yesNumber = @YES;           // equivalent to [NSNumber numberWithBool:YES]
  NSNumber *noNumber = @NO;             // equivalent to [NSNumber numberWithBool:NO]

#ifdef __cplusplus
  NSNumber *trueNumber = @true;         // equivalent to [NSNumber numberWithBool:(BOOL)true]
  NSNumber *falseNumber = @false;       // equivalent to [NSNumber numberWithBool:(BOOL)false]
#endif
}

讨论

NSNumber字面量仅仅支持标量字面量直接跟在'@'后面。因此,@INT_MAX符合规则,但是@INT_MIN则不符合,因为它们的定义像这样子:
#define INT_MAX   2147483647  /* max value for an int */
#define INT_MIN   (-2147483647-1) /* min value for an int */
INT_MIN的定义不是一个简单的字面量,而是一个带括号表达式。带括号表达式可以使用表达式装箱语法装箱,这个将在下一节详细描述。

因为NSNumber当前并不支持讲long double值装箱,使用long double NSNumber字面量(e.g. @123.23L)将被编译器拒绝。

在以前,BOOL类型一般简单的定义为signed char,YES和NO是被定义成展开形式分别为(BOOL)1和(BOOL)0的宏。为了支持@YES和@NO表达式,这些宏定义重新使用<objc/objc.h>中新的语言关键词进行了定义。
#if __has_feature(objc_bool)
#define YES             __objc_yes
#define NO              __objc_no
#else
#define YES             ((BOOL)1)
#define NO              ((BOOL)0)
#endif
编译器会隐式转换 __objc_yes 和 __objc_no 成 (BOOL)1 和 (BOOL)0。这个关键词被用来消除BOOL值和整形字面量间的歧义。

表达式装箱

Objective-C为C表达式装箱提供一条新的语法:
@( <expression> )
标量(数字、枚举、布尔)表达式和C字符串指针都被支持:
// numbers.
NSNumber *smallestInt = @(-INT_MAX - 1);  // [NSNumber numberWithInt:(-INT_MAX - 1)]
NSNumber *piOverTwo = @(M_PI / 2);        // [NSNumber numberWithDouble:(M_PI / 2)]

// enumerated types.
typedef enum { Red, Green, Blue } Color;
NSNumber *favoriteColor = @(Green);       // [NSNumber numberWithInt:((int)Green)]

// strings.
NSString *path = @(getenv("PATH"));       // [NSString stringWithUTF8String:(getenv("PATH"))]
NSArray *pathComponents = [path componentsSeparatedByString:@":"];

枚举装箱

Cocoa框架频繁的使用enums定义常亮值。尽管枚举值都是整形,但是它不可以整个被直接当作字面量装箱(为了避免未来与加‘@’前缀的Objective-C关键词相冲突)。与之相对的,每一个枚举值可以放入装箱表达式中。下面的例子示范了配置AVAudioRecorder时使用包含了装箱的枚举值的字典:
enum {
  AVAudioQualityMin = 0,
  AVAudioQualityLow = 0x20,
  AVAudioQualityMedium = 0x40,
  AVAudioQualityHigh = 0x60,
  AVAudioQualityMax = 0x7F
};

- (AVAudioRecorder *)recordToFile:(NSURL *)fileURL {
  NSDictionary *settings = @{ AVEncoderAudioQualityKey : @(AVAudioQualityMax) };
  return [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:NULL];
}
表达式@(AVAudioQualityMax)将AVAudioQualityMax转换成为整形类型,再对值进行相应的装箱。如果枚举有一个固定的基本类型(fixed underlying type):
typedef enum : unsigned char { Red, Green, Blue } Color;
NSNumber *red = @(Red), *green = @(Green), *blue = @(Blue); // => [NSNumber numberWithUnsignedChar:]
这个固定基本类型将被用在选择正确的NSNumber对象创建方法。
枚举类型装箱时可依照枚举的基本类型选择合适的创建方法创建NSNumber指针,这个基本类型可以是枚举的固定基本类型或者是运行时指定的整形。
typedef enum : unsigned char { Red, Green, Blue } Color;
Color col = Red;
NSNumber *nsCol = @(col); // => [NSNumber numberWithUnsignedChar:]

C字符串装箱

一个C字符串加上“@”前缀表示NSString字面量,同样的,一个数字加上前缀表示NSNumber字面量。当括号表达式里面的类型为char*或const char *时,装箱表达式的结果是包含等价字符数据的NSString指针,以'\0'作为结束,以UTF-8格式编码。下面的例子转换C样式的命令行参数为NSString对象。
// Partition command line arguments into positional and option arguments.
NSMutableArray *args = [NSMutableArray new];
NSMutableDictionary *options = [NSMutableDictionary new];
while (--argc) {
    const char *arg = *++argv;
    if (strncmp(arg, "--", 2) == 0) {
        options[@(arg + 2)] = @(*++argv);   // --key value
    } else {
        [args addObject:@(arg)];            // positional argument
    }
}
像所有的C指针一样,字符指针表达式能进行任意的指针运算,因此,程序员需要确保字符数据是有效的。传一个NULL给字符指针在运行时将导致一个异常。在合适的时候,编译器将拒绝NULL值的字符指针用在装箱表达式中。

容器字面量

Objective-C现在支持新的表达式语法以支持创建不可变的数组和字典容器对象

范例:

不可变的数组表达式
NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ];
这将创建包含3个元素的数组。逗号分格的数据元素字面量支持任意的Objective-C对象指针类型表达式。

不可变的字典表达式
NSDictionary *dictionary = @{
    @"name" : NSUserName(),
    @"date" : [NSDate date],
    @"processInfo" : [NSProcessInfo processInfo]
};
这将创建包含3对键值对的字典。字典字面量的Value子表达式的值必须是Objective-C对象指针类型,跟数组字面量一样。Key子表达式必须是实现了<NSCopying>协议的Objective-C对象指针类型。

讨论

在容器中,key和value的值都不能是nil。如果编译器在编译时能检查到一个key或者value的值为nil,它将发出警告。否则的话,将导致一个运行时错误。
现在使用数组和字典字面量比一般的可变创建表单使用起来更加安全。数组字面量表达式将展开调用 +[NSArray arrayWithObjects:count:],它将娇艳是否所有的对象都非空(non-nil)。在可变创建表单中,使用nil作为参数列表的终止符,这会让数组对象很难看。字典字面量与之类似,使用+[NSDictionary dictionaryWithObjects:forKeys:count:]创建对象,并将检查所有的对象和key的非空。不像+[NSDictionary dictionaryWithObjectsAndKeys:]那样需要使用nil参数值作为参数列表的终止符。

对象下标

Objective-C对象指针值现在可以使用C的下标操作。

范例

下面的demo展示了可变数组和可变字典如何使用对象下标语法:
NSMutableArray *array = ...;
NSUInteger idx = ...;
id newObject = ...;
id oldObject = array[idx];
array[idx] = newObject;         // replace oldObject with newObject

NSMutableDictionary *dictionary = ...;
NSString *key = ...;
oldObject = dictionary[key];
dictionary[key] = newObject;    // replace oldObject with newObject
下一节将说明下表表达式怎样映射到访问器方法。

下标方法

Objective-C支持两种下标表达式:使用整形做下标的数组式下标表达式和使用Objective-C对象指针类型下标的字典式下标表达式。每种类型的下标表达式都通过预先定义的selector映射到指定的message。这样设计的优势在于它的灵活性:类的设计者能在声明方法或者实现协议时能自由的引入下标。此外,由于方法名将根据下标的类型来选择,所以对象能同时支持数组和字典两种加下标的方式。

数组式下标

当下标是整数类型,表达式将使用两个选择器中的一种重写,这依赖于这个元素是读还是写。当表达式是使用整数类型的索引来读取元素,像下面例子中这样:
NSUInteger idx = ...;
id value = object[idx];
它将被转换成调用objectAtIndexedSubscript:
id value = [object objectAtIndexedSubscript:idx];
当表达式使用整数类型索引写入元素值
object[idx] = newValue;
它将被转换成调用setObject:atIndexedSubscript:
[object setObject:newValue atIndexedSubscript:idx];
这些发送的消息将被选择和运行跟显性的消息发送一样。objectAtIndexedSubscript:必须定义一个整数类型参数并且返回一个Objective-C对象指针类型。setObject:atIndexedSubscript:必须定义第一个参数(Objective-C指针类型)和第二个整数类型参数。
索引的意思表明着它是脱离声明的类的。当使用基于类型选择的方法时编译器将确定索引是合适的参数类型。对于一个NSArray的实例,使用超出范围[0, array.count]的索引值去读取元素将导致异常。对于一个NSMutableArray的实例,指定一个元素给范围内的索引位置将替换对应元素,但是指定一个超出范围的索引值给一个元素将导致异常;没有给可变数组提供插入、附加和删除元素的语法。
一个类不需要声明所有方法来获取这种语言特性的优势。比如,NSArray类仅声明ObjectAtIndexedSubscript:因此指定元素将在类型检查时失败,但是,它的子类NSMutableArray声明setObject:atIndexedSubscript:;

字典式下表

当下标操作数是Objective-C对象指针类型时,表达式将使用两个选择器之一,依赖于这个元素是读还是写。当表达式通过使用objective-C对象指针下标读取元素,像下面例子中这样:
id key = ...;
id value = object[key];
它将被转换成调用objectForKeyedSubscript:方法
id value = [object objectForKeyedSubscript:key];
当表达式使用Objective-C对象指针类型下标写入元素时:
object[key] = newValue;
它将被转换成调用setObject:forKeyedSubscript:
[object setObject:newValue forKeyedSubscript:key];
setObject:forKeyedSubscript:的行为是类型指定的,但是一般来说将会覆盖一个已经存在的值,如果指定一个已经绑定的key,否则将为这个key增加一个新的值。没有为可变字典提供删除元素的语法。


Objective-C学习之路-由浅入深

一、Objective-C简介Objective-C 简称OC /Obj-c Objective 是面向对象的,OC是在C语言的基础上添加了一些新的面向对象的语法,比较繁琐的语法封装的更为简单,所以...
  • qq_33750826
  • qq_33750826
  • 2017年02月09日 16:18
  • 1900

黑马程序员——为什么 Objective-C 很难——黑马 ios 技术博客

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 语法: 首先我们谈谈神马叫做编程语言,编程语言是一种让人们能读懂并且能够展现...
  • dumengqiu
  • dumengqiu
  • 2014年12月02日 14:16
  • 498

Objective-C考试2015年12月最新题库的试题

※ 选择题(共80题,每题1分)               1、OC中布尔(BOOL)类型的变量的值只有:答案:(B)             A、1/0             B、YES/NO ...
  • sanzhanggui123
  • sanzhanggui123
  • 2015年12月12日 15:47
  • 2093

使用Xcode工具开发Objective-C程序

1.启动X-code,单击屏幕顶端的File➡️New➡️Project,或者直接点击页面中的Create a new Xcode project, X-code将会弹出如下图所示的新建项目的对话框...
  • tangjun201
  • tangjun201
  • 2015年05月04日 20:50
  • 601

关于swift与objective-c混合使用

详细的swift与objective-c混合使用文档,图解按步说明。
  • xuanwenchao
  • xuanwenchao
  • 2014年06月12日 12:05
  • 3553

iOS开发应该选择Objective-C还是Swift

iOS开发应该选择Objective-C还是Swift,这里会给出你选择:1.如果你想成为一个iOS开发者,你仍然需要学习Objective-C。2.Objective-C比Swift更容易学习。3....
  • qq_31389903
  • qq_31389903
  • 2016年04月08日 08:18
  • 1734

iOS面试题系列之Objective-C相关

1、简述你项目中常用的设计模式。它们有什么优缺点?常用的设计模式有:代理、观察者、单例。(1)单例:它是用来限制一个类只能创建一个对象。这个对象中的属性可以存储全局共享的数据。所有的类都能访问、设置此...
  • yangshebing21
  • yangshebing21
  • 2016年05月02日 00:33
  • 5117

[精通Objective-C]类,接口,协议与扩展

[精通Objective-C]类,接口,协议与扩展参考书籍:《精通Objective-C》【美】 Keith Lee目录精通Objective-C类接口协议与扩展 目录 类 类的接口 类的实现 实例变...
  • sps900608
  • sps900608
  • 2016年06月29日 16:36
  • 1390

在objective-c中实现模板方法模式(template method)

在ios中实现模板方法(template method)模式
  • kyfxbl
  • kyfxbl
  • 2013年12月02日 12:49
  • 3381

Objective-C中的同步线程的锁

概述在多线程编程中往往会遇到多个线程同时访问共享的资源,这种情况我们需要通过同步线程来避免。也就是给线程加锁。 因为Objective-C是C语言的超集。,严格的来说是真超集。所以C语言当中的pth...
  • GGGHub
  • GGGHub
  • 2016年05月09日 16:01
  • 3121
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Objective-C字面量(Objective-C Literals)
举报原因:
原因补充:

(最多只允许输入30个字)