123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- ====================
- Objective-C Literals
- ====================
- Introduction
- ============
- Three new features were introduced into clang at the same time:
- *NSNumber Literals* provide a syntax for creating ``NSNumber`` from
- scalar literal expressions; *Collection Literals* provide a short-hand
- for creating arrays and dictionaries; *Object Subscripting* provides a
- way to use subscripting with Objective-C objects. Users of Apple
- compiler releases can use these features starting with the Apple LLVM
- Compiler 4.0. Users of open-source LLVM.org compiler releases can use
- these features starting with clang v3.1.
- These language additions simplify common Objective-C programming
- patterns, make programs more concise, and improve the safety of
- container creation.
- This document describes how the features are implemented in clang, and
- how to use them in your own programs.
- NSNumber Literals
- =================
- The framework class ``NSNumber`` is used to wrap scalar values inside
- objects: signed and unsigned integers (``char``, ``short``, ``int``,
- ``long``, ``long long``), floating point numbers (``float``,
- ``double``), and boolean values (``BOOL``, C++ ``bool``). Scalar values
- wrapped in objects are also known as *boxed* values.
- In Objective-C, any character, numeric or boolean literal prefixed with
- the ``'@'`` character will evaluate to a pointer to an ``NSNumber``
- object initialized with that value. C's type suffixes may be used to
- control the size of numeric literals.
- Examples
- --------
- The following program illustrates the rules for ``NSNumber`` literals:
- .. code-block:: objc
- 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
- }
- Discussion
- ----------
- NSNumber literals only support literal scalar values after the ``'@'``.
- Consequently, ``@INT_MAX`` works, but ``@INT_MIN`` does not, because
- they are defined like this:
- .. code-block:: objc
- #define INT_MAX 2147483647 /* max value for an int */
- #define INT_MIN (-2147483647-1) /* min value for an int */
- The definition of ``INT_MIN`` is not a simple literal, but a
- parenthesized expression. Parenthesized expressions are supported using
- the `boxed expression <#objc_boxed_expressions>`_ syntax, which is
- described in the next section.
- Because ``NSNumber`` does not currently support wrapping ``long double``
- values, the use of a ``long double NSNumber`` literal (e.g.
- ``@123.23L``) will be rejected by the compiler.
- Previously, the ``BOOL`` type was simply a typedef for ``signed char``,
- and ``YES`` and ``NO`` were macros that expand to ``(BOOL)1`` and
- ``(BOOL)0`` respectively. To support ``@YES`` and ``@NO`` expressions,
- these macros are now defined using new language keywords in
- ``<objc/objc.h>``:
- .. code-block:: objc
- #if __has_feature(objc_bool)
- #define YES __objc_yes
- #define NO __objc_no
- #else
- #define YES ((BOOL)1)
- #define NO ((BOOL)0)
- #endif
- The compiler implicitly converts ``__objc_yes`` and ``__objc_no`` to
- ``(BOOL)1`` and ``(BOOL)0``. The keywords are used to disambiguate
- ``BOOL`` and integer literals.
- Objective-C++ also supports ``@true`` and ``@false`` expressions, which
- are equivalent to ``@YES`` and ``@NO``.
- Boxed Expressions
- =================
- Objective-C provides a new syntax for boxing C expressions:
- .. code-block:: objc
- @( <expression> )
- Expressions of scalar (numeric, enumerated, BOOL), C string pointer
- and some C structures (via NSValue) are supported:
- .. code-block:: objc
- // 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:@":"];
- // structs.
- NSValue *center = @(view.center); // Point p = view.center;
- // [NSValue valueWithBytes:&p objCType:@encode(Point)];
- NSValue *frame = @(view.frame); // Rect r = view.frame;
- // [NSValue valueWithBytes:&r objCType:@encode(Rect)];
- Boxed Enums
- -----------
- Cocoa frameworks frequently define constant values using *enums.*
- Although enum values are integral, they may not be used directly as
- boxed literals (this avoids conflicts with future ``'@'``-prefixed
- Objective-C keywords). Instead, an enum value must be placed inside a
- boxed expression. The following example demonstrates configuring an
- ``AVAudioRecorder`` using a dictionary that contains a boxed enumeration
- value:
- .. code-block:: objc
- 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];
- }
- The expression ``@(AVAudioQualityMax)`` converts ``AVAudioQualityMax``
- to an integer type, and boxes the value accordingly. If the enum has a
- :ref:`fixed underlying type <objc-fixed-enum>` as in:
- .. code-block:: objc
- typedef enum : unsigned char { Red, Green, Blue } Color;
- NSNumber *red = @(Red), *green = @(Green), *blue = @(Blue); // => [NSNumber numberWithUnsignedChar:]
- then the fixed underlying type will be used to select the correct
- ``NSNumber`` creation method.
- Boxing a value of enum type will result in a ``NSNumber`` pointer with a
- creation method according to the underlying type of the enum, which can
- be a :ref:`fixed underlying type <objc-fixed-enum>`
- or a compiler-defined integer type capable of representing the values of
- all the members of the enumeration:
- .. code-block:: objc
- typedef enum : unsigned char { Red, Green, Blue } Color;
- Color col = Red;
- NSNumber *nsCol = @(col); // => [NSNumber numberWithUnsignedChar:]
- Boxed C Strings
- ---------------
- A C string literal prefixed by the ``'@'`` token denotes an ``NSString``
- literal in the same way a numeric literal prefixed by the ``'@'`` token
- denotes an ``NSNumber`` literal. When the type of the parenthesized
- expression is ``(char *)`` or ``(const char *)``, the result of the
- boxed expression is a pointer to an ``NSString`` object containing
- equivalent character data, which is assumed to be '\\0'-terminated and
- UTF-8 encoded. The following example converts C-style command line
- arguments into ``NSString`` objects.
- .. code-block:: objc
- // 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
- }
- }
- As with all C pointers, character pointer expressions can involve
- arbitrary pointer arithmetic, therefore programmers must ensure that the
- character data is valid. Passing ``NULL`` as the character pointer will
- raise an exception at runtime. When possible, the compiler will reject
- ``NULL`` character pointers used in boxed expressions.
- Boxed C Structures
- ------------------
- Boxed expressions support construction of NSValue objects.
- It said that C structures can be used, the only requirement is:
- structure should be marked with ``objc_boxable`` attribute.
- To support older version of frameworks and/or third-party libraries
- you may need to add the attribute via ``typedef``.
- .. code-block:: objc
- struct __attribute__((objc_boxable)) Point {
- // ...
- };
- typedef struct __attribute__((objc_boxable)) _Size {
- // ...
- } Size;
- typedef struct _Rect {
- // ...
- } Rect;
- struct Point p;
- NSValue *point = @(p); // ok
- Size s;
- NSValue *size = @(s); // ok
- Rect r;
- NSValue *bad_rect = @(r); // error
- typedef struct __attribute__((objc_boxable)) _Rect Rect;
- NSValue *good_rect = @(r); // ok
- Container Literals
- ==================
- Objective-C now supports a new expression syntax for creating immutable
- array and dictionary container objects.
- Examples
- --------
- Immutable array expression:
- .. code-block:: objc
- NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ];
- This creates an ``NSArray`` with 3 elements. The comma-separated
- sub-expressions of an array literal can be any Objective-C object
- pointer typed expression.
- Immutable dictionary expression:
- .. code-block:: objc
- NSDictionary *dictionary = @{
- @"name" : NSUserName(),
- @"date" : [NSDate date],
- @"processInfo" : [NSProcessInfo processInfo]
- };
- This creates an ``NSDictionary`` with 3 key/value pairs. Value
- sub-expressions of a dictionary literal must be Objective-C object
- pointer typed, as in array literals. Key sub-expressions must be of an
- Objective-C object pointer type that implements the
- ``<NSCopying>`` protocol.
- Discussion
- ----------
- Neither keys nor values can have the value ``nil`` in containers. If the
- compiler can prove that a key or value is ``nil`` at compile time, then
- a warning will be emitted. Otherwise, a runtime error will occur.
- Using array and dictionary literals is safer than the variadic creation
- forms commonly in use today. Array literal expressions expand to calls
- to ``+[NSArray arrayWithObjects:count:]``, which validates that all
- objects are non-``nil``. The variadic form,
- ``+[NSArray arrayWithObjects:]`` uses ``nil`` as an argument list
- terminator, which can lead to malformed array objects. Dictionary
- literals are similarly created with
- ``+[NSDictionary dictionaryWithObjects:forKeys:count:]`` which validates
- all objects and keys, unlike
- ``+[NSDictionary dictionaryWithObjectsAndKeys:]`` which also uses a
- ``nil`` parameter as an argument list terminator.
- Object Subscripting
- ===================
- Objective-C object pointer values can now be used with C's subscripting
- operator.
- Examples
- --------
- The following code demonstrates the use of object subscripting syntax
- with ``NSMutableArray`` and ``NSMutableDictionary`` objects:
- .. code-block:: objc
- 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
- The next section explains how subscripting expressions map to accessor
- methods.
- Subscripting Methods
- --------------------
- Objective-C supports two kinds of subscript expressions: *array-style*
- subscript expressions use integer typed subscripts; *dictionary-style*
- subscript expressions use Objective-C object pointer typed subscripts.
- Each type of subscript expression is mapped to a message send using a
- predefined selector. The advantage of this design is flexibility: class
- designers are free to introduce subscripting by declaring methods or by
- adopting protocols. Moreover, because the method names are selected by
- the type of the subscript, an object can be subscripted using both array
- and dictionary styles.
- Array-Style Subscripting
- ^^^^^^^^^^^^^^^^^^^^^^^^
- When the subscript operand has an integral type, the expression is
- rewritten to use one of two different selectors, depending on whether
- the element is being read or written. When an expression reads an
- element using an integral index, as in the following example:
- .. code-block:: objc
- NSUInteger idx = ...;
- id value = object[idx];
- it is translated into a call to ``objectAtIndexedSubscript:``
- .. code-block:: objc
- id value = [object objectAtIndexedSubscript:idx];
- When an expression writes an element using an integral index:
- .. code-block:: objc
- object[idx] = newValue;
- it is translated to a call to ``setObject:atIndexedSubscript:``
- .. code-block:: objc
- [object setObject:newValue atIndexedSubscript:idx];
- These message sends are then type-checked and performed just like
- explicit message sends. The method used for objectAtIndexedSubscript:
- must be declared with an argument of integral type and a return value of
- some Objective-C object pointer type. The method used for
- setObject:atIndexedSubscript: must be declared with its first argument
- having some Objective-C pointer type and its second argument having
- integral type.
- The meaning of indexes is left up to the declaring class. The compiler
- will coerce the index to the appropriate argument type of the method it
- uses for type-checking. For an instance of ``NSArray``, reading an
- element using an index outside the range ``[0, array.count)`` will raise
- an exception. For an instance of ``NSMutableArray``, assigning to an
- element using an index within this range will replace that element, but
- assigning to an element using an index outside this range will raise an
- exception; no syntax is provided for inserting, appending, or removing
- elements for mutable arrays.
- A class need not declare both methods in order to take advantage of this
- language feature. For example, the class ``NSArray`` declares only
- ``objectAtIndexedSubscript:``, so that assignments to elements will fail
- to type-check; moreover, its subclass ``NSMutableArray`` declares
- ``setObject:atIndexedSubscript:``.
- Dictionary-Style Subscripting
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- When the subscript operand has an Objective-C object pointer type, the
- expression is rewritten to use one of two different selectors, depending
- on whether the element is being read from or written to. When an
- expression reads an element using an Objective-C object pointer
- subscript operand, as in the following example:
- .. code-block:: objc
- id key = ...;
- id value = object[key];
- it is translated into a call to the ``objectForKeyedSubscript:`` method:
- .. code-block:: objc
- id value = [object objectForKeyedSubscript:key];
- When an expression writes an element using an Objective-C object pointer
- subscript:
- .. code-block:: objc
- object[key] = newValue;
- it is translated to a call to ``setObject:forKeyedSubscript:``
- .. code-block:: objc
- [object setObject:newValue forKeyedSubscript:key];
- The behavior of ``setObject:forKeyedSubscript:`` is class-specific; but
- in general it should replace an existing value if one is already
- associated with a key, otherwise it should add a new value for the key.
- No syntax is provided for removing elements from mutable dictionaries.
- Discussion
- ----------
- An Objective-C subscript expression occurs when the base operand of the
- C subscript operator has an Objective-C object pointer type. Since this
- potentially collides with pointer arithmetic on the value, these
- expressions are only supported under the modern Objective-C runtime,
- which categorically forbids such arithmetic.
- Currently, only subscripts of integral or Objective-C object pointer
- type are supported. In C++, a class type can be used if it has a single
- conversion function to an integral or Objective-C pointer type, in which
- case that conversion is applied and analysis continues as appropriate.
- Otherwise, the expression is ill-formed.
- An Objective-C object subscript expression is always an l-value. If the
- expression appears on the left-hand side of a simple assignment operator
- (=), the element is written as described below. If the expression
- appears on the left-hand side of a compound assignment operator (e.g.
- +=), the program is ill-formed, because the result of reading an element
- is always an Objective-C object pointer and no binary operators are
- legal on such pointers. If the expression appears in any other position,
- the element is read as described below. It is an error to take the
- address of a subscript expression, or (in C++) to bind a reference to
- it.
- Programs can use object subscripting with Objective-C object pointers of
- type ``id``. Normal dynamic message send rules apply; the compiler must
- see *some* declaration of the subscripting methods, and will pick the
- declaration seen first.
- Caveats
- =======
- Objects created using the literal or boxed expression syntax are not
- guaranteed to be uniqued by the runtime, but nor are they guaranteed to
- be newly-allocated. As such, the result of performing direct comparisons
- against the location of an object literal (using ``==``, ``!=``, ``<``,
- ``<=``, ``>``, or ``>=``) is not well-defined. This is usually a simple
- mistake in code that intended to call the ``isEqual:`` method (or the
- ``compare:`` method).
- This caveat applies to compile-time string literals as well.
- Historically, string literals (using the ``@"..."`` syntax) have been
- uniqued across translation units during linking. This is an
- implementation detail of the compiler and should not be relied upon. If
- you are using such code, please use global string constants instead
- (``NSString * const MyConst = @"..."``) or use ``isEqual:``.
- Grammar Additions
- =================
- To support the new syntax described above, the Objective-C
- ``@``-expression grammar has the following new productions:
- ::
- objc-at-expression : '@' (string-literal | encode-literal | selector-literal | protocol-literal | object-literal)
- ;
- object-literal : ('+' | '-')? numeric-constant
- | character-constant
- | boolean-constant
- | array-literal
- | dictionary-literal
- ;
- boolean-constant : '__objc_yes' | '__objc_no' | 'true' | 'false' /* boolean keywords. */
- ;
- array-literal : '[' assignment-expression-list ']'
- ;
- assignment-expression-list : assignment-expression (',' assignment-expression-list)?
- | /* empty */
- ;
- dictionary-literal : '{' key-value-list '}'
- ;
- key-value-list : key-value-pair (',' key-value-list)?
- | /* empty */
- ;
- key-value-pair : assignment-expression ':' assignment-expression
- ;
- Note: ``@true`` and ``@false`` are only supported in Objective-C++.
- Availability Checks
- ===================
- Programs test for the new features by using clang's \_\_has\_feature
- checks. Here are examples of their use:
- .. code-block:: objc
- #if __has_feature(objc_array_literals)
- // new way.
- NSArray *elements = @[ @"H", @"He", @"O", @"C" ];
- #else
- // old way (equivalent).
- id objects[] = { @"H", @"He", @"O", @"C" };
- NSArray *elements = [NSArray arrayWithObjects:objects count:4];
- #endif
- #if __has_feature(objc_dictionary_literals)
- // new way.
- NSDictionary *masses = @{ @"H" : @1.0078, @"He" : @4.0026, @"O" : @15.9990, @"C" : @12.0096 };
- #else
- // old way (equivalent).
- id keys[] = { @"H", @"He", @"O", @"C" };
- id values[] = { [NSNumber numberWithDouble:1.0078], [NSNumber numberWithDouble:4.0026],
- [NSNumber numberWithDouble:15.9990], [NSNumber numberWithDouble:12.0096] };
- NSDictionary *masses = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:4];
- #endif
- #if __has_feature(objc_subscripting)
- NSUInteger i, count = elements.count;
- for (i = 0; i < count; ++i) {
- NSString *element = elements[i];
- NSNumber *mass = masses[element];
- NSLog(@"the mass of %@ is %@", element, mass);
- }
- #else
- NSUInteger i, count = [elements count];
- for (i = 0; i < count; ++i) {
- NSString *element = [elements objectAtIndex:i];
- NSNumber *mass = [masses objectForKey:element];
- NSLog(@"the mass of %@ is %@", element, mass);
- }
- #endif
- #if __has_attribute(objc_boxable)
- typedef struct __attribute__((objc_boxable)) _Rect Rect;
- #endif
- #if __has_feature(objc_boxed_nsvalue_expressions)
- CABasicAnimation animation = [CABasicAnimation animationWithKeyPath:@"position"];
- animation.fromValue = @(layer.position);
- animation.toValue = @(newPosition);
- [layer addAnimation:animation forKey:@"move"];
- #else
- CABasicAnimation animation = [CABasicAnimation animationWithKeyPath:@"position"];
- animation.fromValue = [NSValue valueWithCGPoint:layer.position];
- animation.toValue = [NSValue valueWithCGPoint:newPosition];
- [layer addAnimation:animation forKey:@"move"];
- #endif
- Code can use also ``__has_feature(objc_bool)`` to check for the
- availability of numeric literals support. This checks for the new
- ``__objc_yes / __objc_no`` keywords, which enable the use of
- ``@YES / @NO`` literals.
- To check whether boxed expressions are supported, use
- ``__has_feature(objc_boxed_expressions)`` feature macro.
|