Acquire Foundational Programming Skills获得基本的编程技巧

Acquire Foundational Programming Skills

The Foundation framework, as its name suggests, is the foundational toolkit for all programming for both iOS and OS X. You need to become familiar with this toolkit to be a successful developer for these platforms.

Foundation framework(基本框架),就像它的名字说的,是iOS 和 OS X 所有编程的基础工具包。要想成为这些平台的一个成功开发者,你需要熟悉这个工具箱(toolkit)。

Foundation defines dozens of classes and protocols for a variety of purposes, but three categories of classes and protocols stand out as particularly fundamental:

基本框架 为了各种各样的目的定义了几十个类和协议,但是其中的3个类的类别(categories of classes)和协议(protocols)是基本中的基本:

  • The root class and related protocols. The root class, NSObject, along with a protocol of the same name, specifies the basic interface and behavior of all Objective-C objects. There are also protocols a class can adopt so that clients can copy instances of the class and encode their state.

    根类 以及 相关协议。 根类--NSObject, 有一个跟它同名的协议, 指定了所有Objective-C 对象的基本接口和行为。 也有一个类(class)能采用的各种协议,因此客户端(clients)能复制类的实例以及对它们的状态进行编码。

  • Value classes. A value class generates an instance (called a value object) that is an object-oriented wrapper for a primitive data type such as a string, a number, a date, or binary data. Instances of the same value class with the same encapsulated value are considered equal.

    值类。 一个值类生成一个实例(被称为 值对象),是一个面向对象的基本数据类型包装,比如一个字符串(string), 一个数字(number), 一个日期(date), 或二进制数据(binary date)。相同值类的实例拥有相同包装值则被认为是相等(equal)的。

  • Collection classes. An instance of a collection class (called a collection) manages a group of objects. What distinguishes a particular type of collection is how it lets you access the objects it contains. Often the items in a collection are value objects.

    集合类。 一个集合类的实例(被称为一个集合)管理着一组对象。 区分一个特殊类型的集合方法是它如何让你访问它所包含的对象。 通常在一个集合里的项都是值对象。

Collections and value objects are extremely important in Objective-C programming because you frequently find them as the parameters and return values of methods.

集合 和 值对象在Objective-C编程里极其重要,因为你会经常发现它们被用于方法值和方法参数。

The Root Class and Objective-C Objects

根类 和 Objective-C 对象

In a class hierarchy, a root class inherits from no other class and all other classes in the hierarchy ultimately inherit from it. NSObject is the root class of Objective-C class hierarchies. From NSObject, other classes inherit a basic interface to the Objective-C runtime system. And from NSObject, the instances of these classes derive their fundamental nature as Objective-C objects.

在类继承层次结构里,根类没有父类,而其他所有类都从根类这里开始继承。 NSObject 是Objective-C 类继承结构中的根类。 其它类从NSObject继承一个用于Objective-C runtime系统的基本接口。而且这些类的实例从NSObject那,派生出像Objective-C对象一样的基本性质(fundamental nature).

But by itself, an NSObject instance cannot do anything useful beyond being a simple object. To add any properties and logic specific to your program, you must create one or more classes inheriting from NSObject or from any other class that directly or indirectly inherits from NSObject. The Track class you created while working through My First Mac App directly inherited from NSObject.

但是NSObject实例本身除了作为一个简单的对象不能做任何事。为了向你的程序添加任何特性(properties) 和 特定的逻辑(logic specific),你必须创建一个或多个继承自NSObject的类,或者是直接或间接继承自NSObject的任何其它类。My First Mac App 教程里的Track 类直接继承自NSObject。

NSObject adopts the NSObject protocol, which declares additional methods common to the interfaces of all objects. In addition, NSObject adopts the NSCopyingNSMutableCopying, and NSCoding protocols. When a class adopts these protocols, it augments the basic object behaviors with object-copying and object-encoding capabilities. Model classes—those classes whose instances encapsulate application data and manage that data—frequently adopt the object-copying and object-encoding protocols.

NSObject 类采用NSObject协议, 该协议定义了所有对象接口都通用的附加方法(additional methods)。 此外,NSObject类还采用NSCopying, NSMutableCopying, 和 NSCoding 协议。 当一个类采用这些协议,它就增强了基本对象行为的对象复制(object-copying)和对象的编码能力(object-encoding)。模型类(Model classes)-那些类的实例封装应用程序数据并管理那些数据--常常采用object-copying 和 object-encoding 协议。

The NSObject class and related protocols define methods for creating objects, for navigating the inheritance chain, for interrogating objects about their characteristics and capabilities, for comparing objects, and for copying and encoding objects. Much of the remainder of this article describes the basic requirements for most of these tasks.

NSObject 类 以及相关协议定义了用来 创建对象, 导航继承链,询问对象关于他们的特性和功能, 比较对象, 以及复制和编码对象等各种方法。本文章的很多地方描述了绝大部分这些任务(most of these tasks)的基本要求。

Think in Terms of Objects

从对象角度考虑

At runtime, an app is a network of cooperating objects; these objects communicate with each other to get the work of the app done. Each object plays a role, has at least one responsibility, and is connected to at least one other object. (An object in isolation is of little value.) As illustrated in the figure below, the objects in a network include both framework objects and application objects. Application objects are instances of a custom subclass, usually of a framework superclass. An object network is commonly known as an object graph.

在runtime, 一个应用程序是合作对象网(network of cooperating objects); 这些对象互相通信来完成应用程序的工作。 每个对象代表一个角色, 至少负责一个任务, 至少连接着一个别的对象。(一个隔离(isolation)的对象没什么价值(of little value)) 正如下图所示,在网中的对象包含了框架对象(framework)和应用程序对象(application objects)。 应用程序对象就是一个定义子类的实例, 通常有一个框架超类。 一个对象网就是俗称的对象图。

image: ../Art/app_as_object_network.png

You establish these connections, or relationships, between objects through references. There are many language forms of references, among them instance variables, global variables, and even (within a limited scope) local variables. Relationships can be one-to-one or one-to-many and can express notions of ownership or parent-child correspondence. They are a means for one object to access, communicate with, or control other objects. A referenced object becomes a natural receiver of messages.

你通过引用(references)建立对象之间的这些连接或关系。有很多的引用语言格式,其中包括实例变量,全局变量,和(在有限的范围内)的局部变量。关系可以是 一对一 或 一对多关系,能表达所属关系概念(notions of ownershio) 或者 父子对应(parent-child correspondence)。

Messages between objects in an app are critical to the functional coherence of the app. In a way similar to a musician in an orchestra, each object in an app has a role, a limited set of behaviors it contributes to the app. An object might display an oval surface that responds to taps, or it might manage a collection of data-bearing objects, or it might coordinate the major events in the life of the app. But for its contributions to be realized, it must be able to communicate them to other objects. It must be able to send messages to other objects in the app or be able to receive messages from other objects.

应用里对象之间的消息(messages)对于应用程序的功能一致性(functional coherence)来说是至关重要的。就像一个音乐家在一个乐队里一样,每个对象在应用程序里都有一个角色,它把一组有限的行为(a limited set of behaviors)纳入应用程序。 一个对象可能显示一个相应开发(responds to taps)的椭圆形的表面(an oval surface), 或者它可能管理一个数据承载(data-bearing)对象的集合,或者它可能在应用程序生命期里协调主要事件。但是就其实现性方面的贡献(for its contributions to be realized),它们肯定能跟其它对象交流。它们肯定能在应用程序里向别的对象发送消息, 或者能从别的对象接受信息。

With strongly coupled objects—objects connected through direct references—sending messages is an easy matter. But for objects that are loosely coupled—that is, objects far apart in the object graph—an app has to look for some other communication approach. The Cocoa and Cocoa Touch frameworks feature many mechanisms and techniques enabling communication between loosely coupled objects (as illustrated in the figure below). These mechanisms and techniques, all based on design patterns (which you will learn more about later), make it possible to efficiently construct robust and extensible apps.

强耦合(strongly coupled)对象-- 通过直接应用连接的对象-- 发送消息很简单。但是对于那些松散耦合(loosely coupled)对象--- 就是 在对象图里相隔很远的那些对象-- 应用程序就不得不查询一些别的交流方法(communication approach)。 Cocoa 和 Cocoa Touch 框架采用了很多机制和技术能松散耦合对象之间能够交流(如下图). 这些机制和技术都基于设计模式(你将在稍候学习更多),它们让有效地构建强壮和可扩展的应用程序成为可能。

image: ../Art/communication_loosely_coupled.png

Create Objects

You usually create an object by allocating it and then initializing it. Although these are two discrete steps, they are closely linked. Many classes also let you create an object by calling a class factory method.

创建一个对象,通常通过分配和初始化。 尽管它们是两个分离的步骤,但是他们被紧密连接着。 很多类也允许你通过调用一个类工厂方法(class factory method)来创建一个对象。

Create an Object by Allocating and Initializing It

To allocate an object, you send an alloc message to the object’s class and get back a “raw” (uninitialized) instance of the class. When you allocate an object, the Objective-C runtime allocates enough memory for the object from application virtual memory. In addition to allocating memory, the runtime does a few other things during allocation, such as setting all instance variables to zero.

allocate(分配)一个对象,你给对象类发送一个 alloc 消息,并取回一个"raw"(未初始化)类的实例。当你allocate一个对象时,Objective-C runtime 给对象从应用程序虚拟内存(application virtual memory)里分配足够的内存。另外,在分配内存的同时,runtime 也会做一些别的事情,比如把所有的实例变量设置为0.

 allocating the raw instance, you must initialize it. Initialization sets an object’s initial state—that is, its instance variables and properties—to reasonable values and then returns the object. The purpose of initialization is to return a usable object.

分配完raw 实例后,你必须立即初始化它。初始化设置一个对象的初始状态--也就是它的实例变量和特性---为合理的值后返回给对象。 初始化的目的是返回一个有用的对象。

In the frameworks you will find many methods, called initializers, that initialize objects, but they all have similarities in their form. Initializers are instance methods that begin with init and return an object of type id. The root class,NSObject, declares the init method, which all other classes inherit. Other classes may declare their own initializers, each with its own keywords and parameter types. For example, the NSURL class declares the following initializer:

在框架里你将找到很多方法用来初始化对象,它们被称为initializers(初始化方法),但是它们的格式都有相似之处。初始化方法是以init开始的实例方法,它们返回一个类型为id的对象。 根类,NSObject,声明了所有其他类都要继承的init 方法。 其他类也可能声明它们自己的initializers(初始化方法), 每个初始化方法都有自己的关键字和参数类型。 比如, NSURL类声明了如下初始化方法:

- (id)initFileURLWithPath:(NSString *)path isDirectory:(BOOL)isDir

When you allocate and initialize an object, nest the allocation call inside the initialization call. Using the above initializer as an example:

当你分配和初始化一个对象时,在初始化调用代码里嵌套分配内存调用。以上面的初始化方法为例:

NSURL *aURL = [[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory() isDirectory:YES];

You also allocated and initialized a Track object in when you were creating the TrackMix app:

在TrackMix应用程序你,你也分配和初始化了一个Track对象。

Track *aTrack = [[Track alloc] init];

As a safe programming practice, you can test the returned object to verify that the object was created. If something happens during either stage that prevents the object’s creation, the initializer returns nil. Although Objective-C lets you send a message to nil without negative consequences (for example, without thrown exceptions), your code might not work as expected because no method is called. You should use the instance returned by the initializers, not the one returned by alloc.

作为一次安全编码的实践,你可以测试返回的对象来确认对象却是已经被创建。 如果对象创建失败,则返回nil. 尽管Objective-C允许你给nil发送一个消息,不会产生负面影响(比如,不会抛出异常),但是你的代码将不会如预期那样工作,因为没有方法会被调用。 你应该使用初始化后返回的对象,而不是分配内存后(alloc)返回的对象。

Create an Object by Calling a Class Factory Method

通过一个类工厂方法创建一个对象

You can also create an object by calling a class factory method: a class method whose purpose is to allocate, initialize, and return an instance of itself. Class factory methods are conveniences because they permit you to create an object in one step rather than two. They are of the form:

你也可以通过一个类工厂方法创建一个对象:一个类方法,它的目的是分配内存,初始化,以及返回一个它自身的实例。类工厂方法非常方便,因为它们让你一步就完成创建一个对象,而不需要两步。 它们的格式如下:

  • + (type)className... (where className excludes any prefix--其中className 不包括任何前缀)

Classes of an Objective-C framework sometimes define class factory methods that typically correspond to initializers of the class. For example, NSString declares the following two methods:

那些Objective-C框架的类有时定义类工厂方法通常都根据类的初始化方法。

- (id)initWithFormat:(NSString *)format, ...;
+ (id)stringWithFormat:(NSString *)format, ...;

Here is an example of how you might use this class factory of NSString:

这里有个例子演示了如何使用该NSString类工厂方法:

NSString *myString = [NSString stringWithFormat:@"Customer: %@", self.record.customerName];

Manage the Object Graph to Avoid Memory Leaks

管理对象图来避免内存泄露

The objects in an Objective-C program compose an object graph: a network of objects formed by each object’s relationships with—or references to—other objects. The references an object has are either one-to-one or (via collection objects) one-to-many. The object graph is important because it is a factor in the longevity of objects. The compiler examines the strength of references in an object graph and adds retain and release messages where appropriate.

一个Objective-C程序里的所有对象构成一个对象图:一个由每个对象与其它对象之间的关系或引用形成的对象网。 对象引用关系可能是一对一,或(通过对象集)一对多。对象图很重要因为它是对象生命周期的决定因素。 编译器检查类在对象图里的引用强度,然后添加适当的retain(保留) 和 release(释放) 消息。

Note: Recent versions of the Objective-C runtime implement Automatic Reference Counting (ARC). ARC makes explicit memory management—that is, retaining and releasing objects—unnecessary. You should always use ARC in new application projects, where it is the default.

 注意:Objective-C 运行环境(runtime)的近期版本实现了自动引用计数器(Automatic Reference Counting--ARC)。ARC制造了精确的内存管理系统---也就是,保留对象和释放不必要的对象。你应该使用在新应用程序项目里使用默认的ARC。

You form references between objects through basic C and Objective-C constructs such as global variables, instance variables, and local variables. Each of these constructs carries with it an implied scope; for example, the scope of an object referenced by a local variable is the functional block in which it is declared. Just as importantly, references between objects are either strong or weak. A strong reference indicates ownership; the referring object owns the referenced object. A weak reference implies that the referring object does not own the referenced object. The lifetime of an object is determined by how many strong references there are to it. An object is not freed as long as there is a strong reference to it.

你通过基本C 和 Objective-C结构--比如全局变量,实例变量,和局部变量--来形成对象之间的引用。 每个结构都带有一个隐含范围(implied scope);举个例子,被局部变量引用的一个对象的作用域(scope)是声明该变量的函数块(functional block)。同样重要的是,对象之间的引用是强还是弱。一个强引用表明所属关系(ownership); 引用对象拥有被引用对象。 一个弱引用表明引用对象不拥有被引用对象。 有多少个强引用指向该对象决定了该对象的生命周期(lifetime)。只要有一个指向该对象的强引用存在,该对象就不能被释放。

References in Objective-C are strong by default. Usually this is a good thing, enabling the compiler to manage the runtime life of objects so that objects are not freed while you’re using them. However, if you’re not careful, strong references between objects can form an unbroken chain of references, as illustrated on the left in the diagram below. With such an unbroken chain, it’s possible that the runtime will free none of the objects because there is a strong reference to each of them. Consequently, a strong reference cycle can cause your program to leak memory.

默认情况下,Objective-C里的所有引用都是强引用。 一般情况下,这是一件好事,它能让编译器管理对象的runtime声明周期,让这些对象在你还在使用时不被释放。然而,如果你不小心,对象之间的强引用能形成一个连贯(unbroken)的引用链, 正如下面左图所示。 这样一个连贯的引用链,在整个运行期间(runtime)都可能不释放任何一个对象,因为每个对象都有一个强引用存在。 所以,一个强引用环可能导致你的程序内存泄露。

image: ../Art/strong-ref-cycle-weak-ref.png

For the objects in the figure, if you break the reference between A and B, then the subgraph consisting of B, C, D, and E lives on “forever” because these objects are bound together by a cycle of strong references. By introducing a weak reference from E to B, you break this strong reference cycle.

看左图中的对象,如果你打破A--B之间的引用,然后由B,C,D,和E 组成的子图就会永远("forever")存活,它们的内存不会被系统释放,因为它们被一个强引用组成的环绑定到了一块。 只要在E---B之间引入一个弱引用,你就可以打破该强引用环。

Thus the fix for strong reference cycles is the judicious use of weak references. The runtime keeps track of weak references as well as strong references to an object. Once there are no strong references to an object, it frees that object and sets any weak references to the object to nil. For variables (global, instance, and local), use the __weakqualifier just before the variable name to mark the reference as weak. For properties, use the weak option. You should use weak references for the following kinds of references:

因此,修复强引用环的方法就是明智的使用弱应用。 runtime 会跟检测对一个对象的强引用一样,始终检测那些弱引用。 一旦没有了对该对象的强引用, 它就释放该对象,设置任何指向该对象的弱引用为nil. 对于变量(全局,实例,局部),在变量名前面使用__weak限定词,用来标记该引用时弱引用。 对于特性(properties),使用weak选项。 你应用在以下几种引用使用弱引用:

  • Delegates

    @property (weak) id delegate;

    You will learn about delegates and targets in the Design Patterns article “Streamline Your App with Design Patterns.”

    你将在设计模式文章“Streamline Your App with Design Patterns.”里学习到delegates 和 targets

  • Outlets that are not references to top-level objects

    不是引用顶层对象(top-level objects)的接口(outlets)

    @property (weak) IBOutlet NSString *theName;

    An outlet is a connection (or reference) between objects that is archived in a storyboard or nib file and restored when an app loads the storyboard or nib file. An outlet for a top-level object in a storyboard or nib file—typically a window, view, view controller, or other controller—should be strong (the default, so unmarked).

    接口是对象之间的一个连接(或引用),它在(archived)一个storyboard或nib 文件里存档,当应用程序加载了该storyboard或nib文件之后恢复。在一个storyboard 或 nib 文件里使用的 用于顶层对象的接口---通常是一个window, view, view controller 或其它 controller---应该用强引用(strong--默认已标记)。

    Outlet properties for certain classes of the AppKit framework (for example, NSWindow) are exceptions to this rule; they should be declared with an assign option.

     AppKit 框架的某些类(certain classes)中的接口特性(outlet properties)--比如,NSWindow---例外;它们被声明时应该用assign选项。

  • Targets

    (void)setTarget:(id __weak)target
  • References to self in blocks

    __block typeof(self) tmpSelf = self;
    [self methodThatTakesABlock:^ {
        [tmpSelf doSomething];
    }];

    A block forms a strong reference to variables it captures. If you use self within a block, the block forms a strong reference to self, so if self also has a strong reference to the block (which it typically does), a strong reference cycle results. To avoid the cycle, you need to create a weak (or __block) reference to self outside the block, as in the example above.

     一个块(block)形成一个对那些变量的强引用。如果你在一个块里使用self, 该块形成了对self的一个强引用, 如果self 也有一个对于块的强引用(它通常会这么做),那么一个强引用环就形成了。 为了避免形成强引用环,你应该在块外面对self创建一个弱引用(或 __block引用),就像上面所示。

Manage Object Mutability

管理对象的可变性

A mutable object is one whose state you can change after creating the object. You typically make changes through properties or accessor methods. An immutable object is one whose encapsulated state you cannot change after creating the object. The instances you'll create of most classes of the Objective-C frameworks are mutable, yet a few are immutable. Immutable objects provide you with the following benefits:

可变对象(mutable object)是指创建完该对象之后,你可以改变它的状态。 你通常通过特性和存取方法改变它的状态。不可变对象(an immutable object)是你在创建完该对象后不能改变它的封装状态。你用Objective-C框架里的大多数类创建的实例都是可变的,也有一小部分是不可变的。不可变对象提供了一下优点:

  • An immutable object won’t unexpectedly change in value while you’re using it.

     当你还在使用该对象时,不可变对象值不变。

  • For many types of objects, app performance is improved if the object is immutable.

     对于很多类型的对象,不可变对象将增进应用程序性能。

In Objective-C frameworks, the instances of immutable classes usually are ones that encapsulate collections of discrete or buffered values—for example, arrays and strings. These classes usually have mutable variants with “Mutable” in their names. For example, there is the NSString class (immutable) and the NSMutableString class. Note that for some immutable objects that encapsulate discrete values, such as NSNumber or NSDate, do not have mutable class variants.

 Objective-C 框架中,不可变类的实例常常是那些封装离散集合(encapsulate collections of discrete)或者 缓冲值(buffered values)--比如, arrays(数据) 和 strings(字符串)。 这些类通常有可变变种--名字中含有“Mutable"。举个例子,NSString(不可变)类 有一个 NSMutableString(可变)类的变种。注意,一些用来封装离散数据的不可变对象没有可变类变种,比如 NSNumber 或 NSDate。

Use mutable objects instead of the immutable variant when you expect to change an object’s contents incrementally and frequently. If you receive an object from a framework that is typed as an immutable object, respect that return type; don’t attempt to change the object.

 当你想经常改变一个对象的内容时,使用可变对象 代替 不可变对象。 如果你从框架接收到一个对象,该对象被认为是一个不可变对象,请不要尝试改变该对象。

Create and Use Value Objects

A value object is an object that encapsulates a primitive value (of a C data type) and provides services related to that value. Value objects represent scalar types in object form. The Foundation framework provides you with the following classes that generate value objects for strings, binary data, dates and times, numbers, and other values:

 值对象是封装一个原始值(一个C数据类型) 并提供该值相关的服务的对象。 值对象在对象格式中代表标量类型(scalar types)。 Foundation 框架里提供了以下类,它们给strings, binary date, dates 和 times, numbers, 以及其它值生成值对象。

  • NSString and NSMutableString

  • NSData and NSMutableData

  • NSDate

  • NSNumber

  • NSValue

Value objects are important in Objective-C programming. You frequently encounter these objects as the parameters and return values of methods and functions that your application calls. By passing value objects, different parts of a framework or even different frameworks can exchange data. Because value objects represent scalar values, you can use them in collections and wherever else objects are required. But beyond their commonness and consequent necessity, value objects have an advantage over the primitive types they encapsulate: They enable you to perform certain operations on the encapsulated value in a simple yet efficient manner. The NSString class, for example, has methods for searching for and replacing substrings, for writing strings to files or (preferably) URLs, and for constructing file-system paths.

在Objective-C编程里,值对象很重要。 你经常在你的应用程序调用里遇到这些对象被作为方法和函数的参数和返回值。通过传递值对象, 一个框架内的不同部分,或者甚至是不同框架之间都可以进行数据交换。 因为值对象代表表两只, 你可以在集合(collections)和其它对象任何需要的地方使用它们。但是除了它们的通用性以及随之而来的必要性,值对象还有一个超过它们所封装的原始类型的优点:它们能让你通过简单有效的方式对封装的值进行某些操作。 举个例子, NSString类有查找和替换子字符串的各种方法,有把字符串写入文件或URLs的方法,以及构造文件系统路径(file-system paths)的方法。

Sometimes you’ll find it more efficient and straightforward to use primitive types—that is, values typed as int (integer),float, and so on. A primary example is computing a value. Consequently, NSNumber and NSValue objects are less common as parameters and return values in framework methods. Keep in mind that many of the frameworks declare their own numeric data types and use these types for parameters and return values; examples are NSInteger andCGFloat. You should use these framework-defined types where appropriate because they help you to abstract your code away from the underlying platform.

有时你发现使用原始类型更有效更直接---就是说,直接使用值类型int(integer),float,等等。一个主要的例子就是计算一个值。 所以(consequently), NSNumber 和 NSValue 对象在框架方法中被作为参数和返回值就比较少见。 记住也有一些框架声明了它们自己的数字数据类型(numeric data types),并把这些类型用于它们的参数和返回值声明;比如NSInteger 和 CGFloat。 你应该在适当的地方使用这些框架定义的类型,因为它们会帮助你从底层平台(underlying platform)抽象你的代码。

The Basics of Using Value Objects

值对象的基础使用

The basic pattern for creating a value object is for your code or framework code to create it from data of the primitive type (and then perhaps pass it in a method parameter). In your code, you later access the encapsulated data from the object. The NSNumber class provides the clearest example of this.

创建一个值对象的基本模式是用你的代码或框架代码从原始类型的数据基础上创建它。(然后也许把它传进一个方法参数)。 稍候在你的代码中,从对象访问被封装的数据。 NSNumber类提供了一个最清晰的例子,如下:

int n = 5; // Value assigned to primitive type
NSNumber *numberObject = [NSNumber numberWithInt:n]; // Value object created from primitive type
int y = [numberObject intValue]; // Encapsulated value obtained from value object (y == n)

Most “value” classes declare both initializers and class factory methods for creating their instances. Some classes—NSString and NSData in particular—also provide initializers and class factory methods for creating their instances from primitive data stored in local or remote files as well as from data in memory. These classes also provide complementary methods for writing strings and binary data to files or to URL-specified locations. The code in the following example calls the initWithContentsOfURL: method to create an NSData object from the contents of a file located by a URL object; after using the data, the code writes the data object back to the file system.

大多数”value“类在创建它们的实例时,同时声明初始化方法和类工厂方法。 一些类--特别是NSString 和 NSData --也提供初始化方法和类工厂方法用来从存储在本地或远程文件的原始数据以及内存的数据基础上创建它们的实例。这些类也提供了向文件或URL指定的地址写字符串和二进制数据的完整方法。以下例子中的initWithContentsOfURL:方法用一个URL对象指定位置的一个文件中的内容,创建了一个NSData对象; 使用完这些数据后,代码把数据对象写回到文件系统中。

NSURL *theURL = // Code that creates a file URL from a string path...
NSData *theData = [[NSData alloc] initWithContentsOfURL:theURL];
// use theData...
[theData writeToURL:theURL atomically:YES];

In addition to creating value objects and letting you access their encapsulated values, most value classes provide methods for simple operations such as object comparison.

除了创建值对象并让你访问它们的封装数据之外,大多数值类还提供了简单操作的各种方法,比如对象比较。

When you declare instances of the value classes as properties, you should use the copy option.

当你声明值类(value classes)的实例用来作为特性时,你应该使用copy 选项。

Strings and the NSString Literal

As a superset of C, Objective-C supports the same conventions for specifying strings as does C. In other words, single characters are enclosed by single quotes and strings of characters are surrounded by double quotes. However, Objective-C frameworks typically do not use C strings. Instead, they use NSString objects.

作为C的一个超集, Objective-C也同样支持C语言的指定字符串的约定。换句话说,单个字符用单引号标记,字符串用双引号标记。 然而,通常Objective-C框架不使用C 字符串。相反,它们用NSString对象代理。

The NSString class provides an object wrapper for strings, thereby offering such advantages as built-in memory management for storing arbitrary-length strings, support for different character encodings (particularly Unicode), andprintf-style formatting utilities. Because you commonly use such strings, Objective-C provides a shorthand notation for creating NSString objects from constant values. To use this NSString literal, just precede a normal, double-quoted string with the at sign (@), as shown in the following examples:

NSString 类给字符串做了一个对象包装(object wrapper), 从而提供了这些优势比如存储任意长度字符创的内置内存管理,支持不同的字符编码(尤其是Unicode), 以及printf 风格的格式化工具。 因为你需要常常使用这些字符串,Objective-C提供了一个速记符号用来把常量值创建为NSString对象。为了使用该NSString文字(literal), 只要在一个正常的,用双引号引起来的字串前面加一个@标记,如下图:

// Create the string "My String" plus carriage return.
NSString *myString = @"My String\n";
// Create the formatted string "1 String".
NSString *anotherString = [NSString stringWithFormat:@"%d %@", 1, @"String"];
// Create an Objective-C string from a C string.
NSString *fromCString = [NSString stringWithCString:"A C string" encoding:NSASCIIStringEncoding];

NSNumber Literals

Objective-C also offers a shorthand notation for creating NSNumber objects, removing the need to call initializers or class factory methods to create such objects. Simply precede the numeric value with the at sign (@) and optionally follow it with a value-type indicator. For example, to create NSNumber objects encapsulating an integer value and a double value, respectively, you could write the following:

Objective-C 同样提供了一个快速符号用来创建NSNumber对象,而不需要调用初始化函数或者类工厂方法。只需简单在数字前面加一个@,并且可以选择性的在后面跟上一个值类型指示器(value-type indicator). 举个例子,分别创建两个封装了一个integer值和一个double值的NSNumber 对象,可以按以下方法编写:

NSNumber *myIntValue    = @32;
NSNumber *myDoubleValue = @3.22346432;

You can even use NSNumber literals to create encapsulated Boolean and character values.

你甚至可以使用NSNumber文本创建封装了Boolean值和字符值的NSNumber对象。

NSNumber *myBoolValue = @YES;
NSNumber *myCharValue = @'V';

You can create NSNumber objects representing unsigned integers, long integers, long long integers, and float values by appending the letters “U”, “L”, “LL”, and “F”, respectively, to the notated value. For example, to create an NSNumberencapsulating a float value, you could write the following:

你可以在创建时在后面追加"U","L", "LL", "F"表示unsigned integers, long integers, long long integers, 以及 float 值。比如,创建一个封装了一个float值的NSNumber对象,你可以按下面编写:

NSNumber *myFloatValue = @3.2F

Dates and Times

An NSDate object is different from other kinds of value objects because of the distinctive nature of time as a primitive value. A date object encapsulates the interval, in seconds, from a reference date. That reference date is the first instant of January 1, 2001 GMT.

NSDate对象跟其它任何值对象都不同,因为时间作为一个原始值的独特性。 date对象根据一个参考日期封装了间隔时间(interval),以秒为单位。该基准日是GMT(格林威治时间) 2001年1月1号为第一个瞬间(instant)。

You can do little with an instance of NSDate just by itself. It does represent a moment in time, but that representation is without the context provided by a calendar, a time zone, and the temporal conventions of a locale. Fortunately, there are Foundation classes representing these conceptual entities:

单NSDate实例本身用处很少。 它确实表示一瞬间的时间,但是它代表的时间跟日历,时区,和一个locale的区域时间约定毫无关系(temporal conventions of a locale)。所幸,有表示这些概念实体(conceptual entities)的基础类:

  • NSCalendar and NSDateComponents—You can associate the date with a calendar and then derive temporal units from the calendar for the date such as year, month, hour, and day of week. You can also perform calendrical calculations.

    NSCalender 和 NSDateComponents---你可以把一个日期跟日历相关联,然后从日历获得那个日期的时间单位(temporal units),比如年,月,时,一星期的第一天等。 你也可以执行历法计算。

  • NSTimeZone—When a date and time must reflect the time zone of an area, you can associate a time-zone object with a calendar.

    NSTimeZone---当一个日期很时间必须体现一个区域的时区时,你可以把一个时区对象(time-zone object)跟一个日历相关联。

  • NSLocale—A locale object encapsulates cultural and linguistic conventions, including those related to time.

    NSLocale---一个locale对象封装了那些与时间相关的文化和语言习惯。

The following code snippet illustrates how you can use an NSDate object with these other objects to obtain the information you want (in this case, the current time printed as hours, minutes, and seconds). Refer to the numbered list below the code for an explication.

以下代码片段你可以如何使用一个NSDate对象和这些其它对象去获得你想要的信息(在本例,当前时间被以时,分,秒格式打印)。请参考下面的编号列表做进一步的解释:

NSDate *now = [NSDate date]; // 1
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; // 2
[calendar setTimeZone:[NSTimeZone systemTimeZone]]; // 3
NSDateComponents *dc = [calendar components:(NSHourCalendarUnit|NSMinuteCalendarUnit|
    NSSecondCalendarUnit) fromDate:now];  // 4
NSLog(@"The time is %d:%d:%d", [dc hour], [dc minute], [dc second]); // 5
  1. Creates a date object representing the current instant.

    创建一个日期对象表示当前时间瞬间。

  2. Creates an object representing the Gregorian calendar.

    创建一个对象代表公历(Gregorian calendar)

  3. Sets the calendar object with an object representing the time zone specified in System Preferences.

    用系统偏好设置(System Preferences)里指定的时区设置日期对象。

  4. Calls the components:fromDate: method on the calendar object, passing in the date object created in step 1. This call returns an object containing the hour, minute, and second components of the date object.

    调用calender对象中的components:fromDate: 方法, 传入步骤1中创建的日期对象。该调用返回一个包含时,分,秒组件的日期对象。

  5. Logs the current hour, minute, and second to the console.

    在控制台以时,分,秒格式输出当前时间。

Although this example logs the result, the preferred approach for displaying date information in an app’s user interface is to use date formatters (instances of the NSDateFormatter class). You should always use the appropriate class and method for calendrical calculations; don’t hard-code numerical values for units such as minutes, hours, and days.

尽管本例输出了结果,但是在一个应用程序用户界面里显示日期信息的首选方法是使用date formatters(NSDateFormatter 类的实例)。 你应该始终使用适当的类和方法来进行历法计算;不要对时间单位比如分(minutes),时(hours),天(days)等数值进行硬性编码。

Create and Use Collections

A collection is an object that stores other objects in a certain way and allows clients to access those objects. You often pass collections as parameters of methods and functions, and you often obtain collections as return values of methods and functions. Collections frequently contain value objects, but they can contain any type of object. Most collections have strong references to the objects they contain.

集合是一个对象(object),它以特定方式存储其它对象,并允许用户访问这些对象。你常常把集合(collections)作为方法和函数的参数,也常常作为返回值从方法和函数返回。 集合通常包含值对象,但是它们实际上能包含任何类型的对象。 大多数集合对于它们包含的对象拥有强引用(strong references)。

The Foundation framework has several types of collections, but three of them are particularly important in Cocoa and Cocoa Touch programming: arrays, dictionaries, and sets. The classes for these collections come in immutable and mutable variants; mutable collections permit you to add and remove objects, but immutable collections can contain only the objects they were created with. All collections allow you to enumerate their contents—in other words, to examine each of the contained objects in turn.

Foundation 框架中有好几种类型的集合, 但是它们其中的3种在Cocoa 和 Cocoa Touch编程里尤其重要:arrays(数组),dictionaries(字典), 和 sets(集)。 这些集合的类包含不可变变种(immutable variants)和可变变种(mutable variants);可变集合(mutable collections)允许你添加和删除对象, 但是不可变集合(immutable collections)只包含它们初始化时包含的那些对象。 所有集合都允许你遍历(enumerate--枚举)它们的内容,就是说,允许你挨个检查它们包含的对象。

Different types of collections organize their contained objects in distinctive ways:

不同类型的集合以它们独特的方式组织它们包含的对象:

  • NSArray and NSMutableArray—An array is an ordered collection of objects. You access an object by specifying its position (that is, its index) in the array. The first element in an array is at index 0 (zero).

    NSArray 和 NSMutableArray --- 数组是对象的一个有序集合(ordered collections)。在数组里,你可以通过指定它的位置(它的index)访问一个对象。数组的第一个元素index为0.

  • NSDictionary and NSMutableDictionary—A dictionary stores its entries as key-value pairs; the key is a unique identifier, usually a string, and the value is the object you want to store. You access this object by specifying the key.

    NSDictionary 和 NSMutableDictionary --- 字典以键值对(key-value pairs)存储它的全部对象;key-键是一个唯一的标识符,常常是一个字符串, value-值是你想存储的对象。你可以通过指定的键访问该对象。

  • NSSet and NSMutableSet—A set stores an unordered collection of objects, with each object occurring only once. You generally access objects in the set by applying tests or filters to objects in the set.

    NSSet 和 NSMutableSet --- set-集存储了一组无序的对象, 每个对象都不重复。你一般通过在set里 申请(applying)测试(tests) 或 过滤器(filters)的方式访问set里的对象。

image: ../Art/collections.png

Because of their storage, access, and performance characteristics, one type of collection can be better suited to a particular task than another one.

正因为它们的存储,访问和性能特性的不同, 每种类型的集合够各自适合某种特别的任务,其优点是别的种类的集合无法比拟的。

You can create arrays and dictionaries and access the values they contain either by calling methods of NSArray andNSDictionary or by using special Objective-C container literals and subscripting techniques. The following sections describe both approaches.

你可以通过2种方式创建数组和字典并访问它们包含的值。一种是通过调用NSArray 和 NSDictionary 类的方法, 或者通过使用特殊的Objective-C容器文本(container literals) 和 下标技术(subscripting techniques)。以下介绍了这两种方法:

Store Objects in a Certain Order in Arrays

按照一定顺序在数组里存储对象

Arrays store objects in an ordered sequence. Thus, you use arrays when the order of objects in the collection is important. For example, many apps use arrays to give content to the rows of a table view or the items in a menu; the object at index 0 corresponds to the first row, the object at index 1 corresponds to the second row, and so on. Access times for objects in arrays are slower than they are for objects in a set.

数组按有序序列存储对象。因此,当对象的顺序很重要使,选择用arrays(数组)。举个例子,很多应用程序给一个table view里的rows(行)或一个menu(菜单)里的items(项)采用数组;index 0 的数组对象对应table view 的第一行,index 1 的对象对应第二行,以此类推。 访问数组里对象的速度比访问set里对象的速度慢。

Creating Arrays

The NSArray class gives you many initializers and class factory methods for creating and initializing arrays, but a few methods are particularly common and useful. You can create an array from a series of objects with thearrayWithObjects:count: and arrayWithObjects: methods (and their corresponding initializers). With the former method, the second parameter specifies the number of objects in the first parameter; with the latter method, you terminate the comma-separated series of objects with nil.

NSArray 类提供了很对创建和初始化数组的方法和类工厂方法,但是特别常见和有用的很少。 你可以用方法 arrayWithObjects:count: 和 arrayWithObjects: (以及相关的初始化方法)方法来创建一个数组。 前一个方法中的第二个参数(count:)指定了第一个参数(arrayWithObjects:)所包含的对象个数;第二个方法中,你以逗号分隔各个对象,并以nil结束。

// Compose a static array of string objects
NSString *objs[3] = {@"One", @"Two", @"Three"};
// Create an array object with the static array
NSArray *arrayOne = [NSArray arrayWithObjects:&(*objs) count:3];
// Create an array with a nil-terminated list of objects
NSArray *arrayTwo = [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];

When creating mutable arrays, you can use the arrayWithCapacity: (or initWithCapacity:) method to create the array. The capacity parameter gives a hint to the class about the expected size of the array, thus making the array more efficient at runtime. Moreover, the array can exceed the specified capacity.

当你创建可变数组时,你可以使用arrayWithCapacity:(或 initWithCapacity:)方法。 capacity 参数指定了数组所期望的对象个数,从而让数组在runtime更有效率。

You can also create arrays using the container literal @[], where the items between the brackets are comma-separated objects. For example, to create an array containing a string, a number, and a date, you could write the following:

你也可以用容量文本@[...]创建数组, 中括号里的项是数组包含的对象,以逗号分隔。 比如,以下演示了创建一个包含一个字符串,一个数字,以及一个日期的数组:

NSArray *myArray = @[ @"Hello World", @67, [NSDate date] ];

Accessing Objects in Array

Generally, you call the objectAtIndex: method to access an object in an array by specifying its index position (zero-based) in the array:

通常,你通过调用objectAtIndex:方法,指定它的index 位置(以0开头)访问数组里的对象。

NSString *theString = [arrayTwo objectAtIndex:1]; // returns second object in array

NSArray gives you other methods to access either the objects in an array or their indexes. For example, there islastObjectfirstObjectCommonWithArray:, and indexOfObjectPassingTest:.

NSArray 提供了其它方法,用来访问数组中的对象 或 它们的 indexes(索引值,下标). 比如,lastObject, firstObjectCommonWithArray: 以及 indexOfObjectPassingTest: 方法。

Instead of using a method of NSArray to access an object in an array, you can use subscripting notation. For example, to access the second object in myArray (created above), you might write something like this:

你也可以用下标符号(subscripting notation)代替NSArray类的方法来访问数组中的对象。 比如,访问myArray(上面创建的例子)的第二个对象,你可以按以下方式编写:

id theObject = myArray[1];

Another common task with arrays is to do something with each of the objects in the array—this is a procedure known as enumeration. You often enumerate arrays to determine whether an object or objects match a certain value or condition and, if one does, complete an action with it. You can take one of three approaches for enumerating arrays: fast enumeration, enumeration with a block, or using an NSEnumerator object. Fast enumeration, as its name implies, is typically faster than using other techniques to access the objects in an array. Fast enumeration is a language feature that requires a specific syntax:

数组的另一个常见任务是数组里的对象互相之间做些操作--- 这个过程被称为遍历(enumeration). 你尝尝需要遍历一个数组来决定一个或多个对象是否跟一个特定值或特定情况相匹配, 如果匹配则对它完成一个动作(action)。 有3种遍历数组的方法:快速枚举(fast enumeration), 枚举块(enumeration with a block) 和 使用NSEnumerator 对象, 你可以选择其中合适的方法使用。 Fast enumeration,正如它的名字所示 它通常比使用其它技术访问一个数组要快。 Fast enumeration 是要求特定语法的一个语言特性(language feature)。

for (type variable in array)/* inspect variable, do something with it */ }

For example(举例):

NSArray *myArray = // get array
for (NSString *cityName in myArray) {
    if ([cityName isEqualToString:@"Cupertino"]) {
        NSLog(@"We're near the mothership!");
        break;
    }
}

Several NSArray methods enumerate arrays with blocks, the simplest of them being enumerateObjectsUsingBlock:. The block has three parameters: the current object, its index, and a by-reference Boolean value, which if set to YESterminates the enumeration. The code in the block performs exactly the same work as the code between the braces in a fast-enumeration statement.

一些NSArray方法 用块遍历数组, 其中最简单的是 enumerateObjectsUsingBloack: 方法。 块(block)有3个参数:当前对象(current object), 它的索引值(its index), 以及一个被引用的布尔值(a by-reference Boolean value)---设置为YES则终止遍历(enumeration)。在块中的代码跟以上大括号中fast-enumertion语句做一样的工作。

NSArray *myArray = // get array
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if ([obj isEqual:@"Cupertino"]) {
        NSLog(@"We're near the mothership!");
        *stop = YES;
    }
}];

Managing Mutable Arrays

管理可变数组

NSArray has other methods that sort arrays, search arrays, and invoke a method on each object in an array.

NSArray包含了其它各种方法, 比如数组排序,数组搜索,给数组中的每个对象调用一个方法等。

You can add an object to a mutable array by calling the addObject: method; the object is placed at the end of the array. You can also use insertObject:atIndex: to put an object at a particular location in a mutable array. You can remove an object from a mutable array by calling the removeObject: method or the removeObjectAtIndex: method.

你可以调用addObject:方法给一个可变数组添加一个对象;添加的对象被加到数组最尾端。 你也可以用insertObject:atIndex: 方法把一个对象插入一个可变数组的特定位置。 你可以调用removeObject:方法 或 removeObjectAtIndex: 方法从一个可变数组删除一个对象。

You can also use subscripting notation to insert an object at a specific location in a mutable array.

你也可以用下标符号方法把一个对象插入可变数组的指定位置。

NSMutableArray *myMutableArray = [NSMutableArray arrayWithCapacity:1];
NSDate *today = [NSDate date];
myMutableArray[0] = today;

Store Key-Value Pairs in Dictionaries

You use a dictionary to store objects as key-value pairs—that is, an identifier (a key) is paired with an object (a value). Dictionaries are unordered collections because the key-value pairs can be in any order. Although a key can be virtually anything, it is typically a string that describes the value—for example, NSFileModificationDate orUIApplicationStatusBarFrameUserInfoKey (which are string constants). When there are public keys, dictionaries are a great way to pass information of any kind between objects.

使用字典以键值对(key-value pairs)形式存储对象--就是说,一个标识符(a key) 跟一个对象(a value)相配对。字典是一些无序集合,因为键值对可以以任何顺序放置。 尽管键可以是任何东西,但是通常是一个描述它所配对值的字符串---比如,NSFileModificationDate 或 UIApplicationStatusBarFrameUserInfoKey (它们都是字符串常量)。 当它们是公共键时, 字典里的对象之间将以显著的方式(a great way)传递任何信息。

Creating Dictionaries

Through its initializers and class factory methods, the NSDictionary class gives you many ways to create dictionaries, but two class methods are particularly common: dictionaryWithObjects:forKeys: anddictionaryWithObjectsAndKeys: (or their corresponding initializers). With the former method, you pass in an array of objects and an array of keys; the keys are positionally matched with their values. With the second method, you specify the first object value and then its key, the second object value and then its key, and so on; you signal the end of this series of objects with nil.

尽管NSDictionary类提供了很多创建字典的初始化方法和类工厂方法, 但是只有其中的两个类方法比较通用:dictionaryWithObjects:forKeys: 和 dictionaryWithObjectsAndKeys:(以及相关的初始化方法)。前一个方法,传进一个对象数组和一个键数组;键值按位置跟它们的值相对应。 第二个方法, 你指定第一个对象及它的键,第二个对象及它的键,以此类推;以nil结束。

// First create an array of keys and a complementary array of values
NSArray *keyArray = [NSArray arrayWithObjects:@"IssueDate", @"IssueName", @"IssueIcon", nil];
NSArray *valueArray = [NSArray arrayWithObjects:[NSDate date], @"Numerology Today",
    self.currentIssueIcon, nil];
// Create a dictionary, passing in the key array and value array
NSDictionary *dictionaryOne = [NSDictionary dictionaryWithObjects:valueArray forKeys:keyArray];
// Create a dictionary by alternating value and key and terminating with nil
NSDictionary *dictionaryTwo = [[NSDictionary alloc] initWithObjectsAndKeys:[NSDate date],
    @"IssueDate", @"Numerology Today", @"IssueName", self.currentIssueIcon, @"IssueIcon", nil];

As with arrays, you can create an NSDictionary object by using the container literal @{key : value, …} where “…” indicates any number of key-value pairs. For example, the following code creates an immutable dictionary object with three key-value pairs:

跟数组一样,你可以用容器文本(container literal)@{key : value,....}来创建一个NSDictionary 对象,其中"..."表明任何数目的键值对。 比如,以下代码创建了一个有3个键值对的不可变字典对象:

NSDictionary *myDictionary = @{
   @"name" : NSUserName(),
   @"date" : [NSDate date],
   @"processInfo" : [NSProcessInfo processInfo]
};

Accessing Objects in Dictionaries

You access an object value in a dictionary by calling the objectForKey: method, specifying a key as a parameter.

你可以通过调用objectForKey:方法访问一个字典中对象值,指定一个键作为参数。

NSDate *date = [dictionaryTwo objectForKey:@"IssueDate"];

You can also access an object in a dictionary using subscripting. The key appears between brackets right after the dictionary variable.

你也可以通过下标访问一个字典中的对象。 在字典变量右边的中括号内填入键(key)。

NSString *theName = myDictionary[@"name"];

Managing Mutable Dictionaries

You can insert and delete items in mutable dictionaries by calling the setObject:forKey: and removeObjectForKey:methods. setObject:forKey: replaces any existing value for the given key. These methods are fast.

你可以调用setObject:forKey: 和 removeObjectForKey: 方法来插入和删除在可变字典里的项。 setObject:forKey: 根据给定的key替换任何已经存在的值。 这些方法运行速度很快。

You can also use subscripting to add a key-value pair to a mutable dictionary. The key is subscripted on the left side of the assignment, and the value is on the right side.

你也可以用下标向一个可变字典添加一个键值对。 键下标在赋值语句的左边,值在右边。如下:

NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
mutableDict[@"name"] = @"John Doe";

Store Unordered Objects in Sets

在sets里存储无序对象

Sets are collection objects similar to arrays except the items they contain are unordered instead of ordered. Rather than access objects in the set by index location or through a key, you access them randomly (anyObject), by enumerating the collection, or by applying a filter or test to the set.

Sets存储对象的方式跟数组很相似,除了它所包含的对象是无序而不是有序的。 你访问sets里的对象是随机的(任何对象),通过遍历整个集合 或 给set申请一个过滤器(filter)或测试(test)。

Although set objects are not as common in Objective-C programming as dictionaries and arrays, they are an important collection type in certain technologies. In Core Data (a data-management technology), when you declare a property for a to-many relationship, the property type should be NSSet or NSOrderedSet.

尽管set 对象在Objective-C 编程里不像字典和数组那样普遍,但是它们在一些特定技术里是一个很重要的集合类型。 在Core Data(一种数据管理技术)里面,当你为一个 对多关系(to-many relationship)声明一个特性(property)时,该特性类型应该为NSSet 或 NSOrderedSet。

Ordered sets are an exception to the basic definition of a set. In an ordered set, the order of the items in the set is important. Testing for membership in an ordered set is faster than it is in an array.

Ordered sets 是一个set定义的例外。 在一个ordered set(有序集)里,set里的项顺序是很重要的。 在一个有序集里测试成员关系比在一个数组里测试快。

Verify Object Capabilities at Runtime

Introspection, a powerful and useful feature of Objective-C and the NSObject class, enables you to learn certain things about objects at runtime. You can thus avoid mistakes in your code such as sending a message to an object that doesn’t recognize it or assuming that an object inherits from a given class when it doesn’t.

总结,一个强大并有用的Objective-C 和 NSObject类功能,能让你学习关于对象在runtime时的一些东西(certain things)。从而,你就可以避免在代码中出错,比如发送一个消息给一个不认识的对象, 或假设一个对象从一个没给定的类继承。

There are three important types of information that an object can divulge about itself at runtime:

有3中重要类型的信息可以透露一个对象本身:

  • Whether it’s an instance of a particular class or subclass

    是否是一个特殊类或子类的一个实例

  • Whether it responds to a message

    它是否对一消息做出反应

  • Whether it conforms to a protocol

    它是否遵守一个协议

Discover Whether an Object Is an Instance of a Particular Class or its Subclasses

检测一个对象是否是一个特殊类或其子类的一个实例

To discover whether an object is an instance of a class or its subclasses, call the isKindOfClass: method on the object. An app sometimes makes this check when it wants to discover the messages (implemented or inherited) that an app responds to.

为了检测一个对象是否是一个特殊类或其子类的一个实例, 调用对象的 isKindOfClass: 方法。 一个应用程序有时会检查这项,当它想检测该应用程序回应(responds to)的那些消息(实现 或 继承)时。

static int sum = 0;
for (id item in myArray) {
    if ([item isKindOfClass:[NSNumber class]]) {
        int i = (int)[item intValue];
        sum += i;
    }
}

The isKindOfClass: method takes an object of type Class as a parameter; to get this object, call the class method on the class symbol. Evaluate the Boolean value returned by this method and proceed accordingly.

isKindOfClass: 方法带有一个Class类型的对象参数; 想获得该对象,调用类符号(比如 NSNumber)的 class方法. 评估此方法返回的布尔值,并进行相应处理。

NSObject declares other methods for discovering information about object inheritance. The isMemberOfClass:method, for example, tells you whether an object is an instance of a specific class, whereas isKindOfClass: tells you whether the object is a member of that class or any of its descendent classes.

NSObject类声明了一些其它方法,用来检测对象继承的信息。 比如,isMemberOfClass: 方法 告诉你一个对象是否是一个指定类的一个实例, 而isKindOfClass:方法告诉你该对象是否是那个类的一个成员或后代成员。

Discover Whether an Object Responds to a Message

检测是否一个对象响应了一个消息

To do this, call the respondsToSelector: method on the object. App code often verifies that an object responds to a message before it sends the message to the object.

调用对象的respondsToSelector:方法来实现。 应用代码经常在它向那个对象发送消息时,认证一个对象是否对一个消息做出反应。

if ([item respondsToSelector:@selector(setState:)]) {
    [item setState:[self.arcView.font isBold] ? NSOnState : NSOffState];
}

The respondsToSelector: method takes a selector as its parameter. A selector is an Objective-C data type for runtime identifiers of methods; you specify a selector using the @selector compiler directive. In your code, evaluate the Boolean value returned by this method and proceed accordingly.

respondsToSelector: 方法带有一个 selector参数。 selector 是一个Objective-C数据类型,是各种方法的runtime标识符;你可以用@selector编译器指令指定一个selector。 在你的代码中, 评估这个方法返回的布尔值,并做出相应的处理。

For identifying the messages an object responds to, calling respondsToSelector: is generally more useful than evaluating class type. For example, a more recent version of a class might implement a method that isn’t found in a prior version.

调用respondsToSelector: 方法来识别一个对象响应的消息,比评估它的class 类型更有用。 比如, 一个类的更新的一个版本中可能实现了一个在先前的版本中没有的方法。

Discover Whether an Object Conforms to a Protocol

检测一个对象是否遵循一个协议

To do this, call the conformsToProtocol: method on the object.

调用对象的conformsToProtocol:方法来检测。

- (void) setDelegate:(id __weak) obj {
    NSParameterAssert([obj conformsToProtocol:
        @protocol(SubviewTableViewControllerDataSourceProtocol)]);
    delegate = obj;
}

The conformsToProtocol: method takes a runtime identifier of a protocol as a parameter; you specify this identifier using the @protocol compiler directive. Evaluate the Boolean value returned by this method and proceed accordingly. Note than an object can conform to a protocol without implementing its optional methods.

conformsToProtocol: 方法带有一个协议的runtime标识符作为参数;你可以用@protocol 编译器指令 指定该标识符。 评估该方法返回的布尔值并做出相应处理。 注意,一个对象能遵守一个协议而不需要实现该协议的可选方法。

Compare Objects

You can compare two objects by using the isEqual: method. The object receiving the message is compared to the passed-in object; if they’re the same, the method returns YES. For example:

你可以调用isEqual: 方法比较2个对象。 接收消息的对象(被比较) 与 传入(passed-in)的对象进行比较;如果它们一样(same),则返回YES. 比如:

BOOL objectsAreEqual = [obj1 isEqual:obj2];
if (objectsAreEqual) {
    // do something...
}

Note that object equality is different from object identity. For the latter, use the equality operator (==) to test whether two variables point to the same instance.

注意对象相等(equality)跟对象恒等(identity)是不一样的。后者,用相等运算符(==)来测试两个变量是否指向同一个实例。

What is compared when you compare two objects of the same class? That depends on the class. The root class,NSObject, uses pointer equality as the basis of comparison. Subclasses at any level can override their superclass’s implementation to base the comparison on class-specific criteria, such as object state. For example, a hypothetical Person object might equal another Person object if the first-name, last-name, and birth-date attributes of both objects match.

当你比较2个相同(same)的类时,比较了什么? 那依赖于类。 基类,NSObject, 把指针相等(pointer equality)作为比较的基础。任何级的子类可以覆盖(override)它们的超类对于基础比较的实现作为特定类的标准。比如,假设一个Person对象 可能跟另一个Person对象相等,如果2个对象的first-name, last-name 和 birth-date都相等的话。

The value and collection classes of the Foundation framework declare comparison methods of the formisEqualToType:, where Type is the class type minus the “NS” prefix—for example, isEqualToString: andisEqualToDictionary:. The comparison methods assume that the passed-in object is of the given type and raise an exception if it is not.

Foundation 框架的值和集合类(collection classes) 声明了比较方法---isEqualToType:, 其中的Type 是class 类型 加上“NS”前缀---比如, isEqualToString: 和 isEqualToDictionary:方法。 这些比较方法都假设传入的对象是已经有给定类型,否则将抛出一个异常。

Copy Objects

You make a copy of an object by sending a copy message to it:

你通过传递一个copy 消息来复制一个对象:

NSArray *myArray = [yourArray copy];

To be copied, the class of the receiving object must conform to the NSCopying protocol. If you want your objects to be copyable, you must adopt and implement the copy method of this protocol.

为了复制成功,接收对象的类必须遵守NSCopying协议。 如果你想要你的对象是可复制的,你必须采用并实现该协议的copy方法。

You sometimes copy an object obtained from elsewhere in a program when you want to ensure that the object’s state does not change while you’re using it.

有时,你需要从程序的某处拷贝获得一个对象,从而确保那个对象的状态在你使用的时候不会被改变。

Copying behavior is specific to a class and depends upon the specific nature of the instance. Most classes implement deep copying, which makes a duplicate of all instance variables and properties; some classes (for example, the collection classes) implement shallow copying, which only duplicates the references to those instance variables and properties.

复制行为是一个类的特性,它取决于实例的具体性质。 大多数类实现深拷贝(deep copying), 它制作了所有实例变量和特性的一个副本; 一些类(比如, 各种集合类)实现浅拷贝(shallow copying),它只复制了对那些实例变量和特性的引用。

Classes that have mutable and immutable variants also declare a mutableCopy method to create a mutable copy of an object. For example, if you call mutableCopy on an NSString object, you get an instance of NSMutableString.

原文地址:https://www.cnblogs.com/patientAndPersist/p/3121599.html