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.
|