forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			549 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			549 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
| ====================
 | |
| 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) and C string pointer
 | |
| types 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:@":"];
 | |
| 
 | |
| 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.
 | |
| 
 | |
| 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
 | |
| 
 | |
| 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.
 |