Objective -C Categories
The dynamic runtime dispatch mechanism employed by Objective-C lets you add methods to existing classes.
被Objective -C 采纳的动态运行分配机制让你可以在已有的类中添加方法。
1.1 Creating a Category 创建一个Category
A category is a way to add new methods to existing classes.
category 是你往已有的类中添加方法的一种方式。
You can do this to any class, even classes you don't have the source code for.
你可以往任何类甚至你不知道源码的类中添加分类
NSNumber *number;
number = [NSNumber numberWithUnsignedInt: [string length]];
// ... do something with number
Programmers often put categories in their own files, typically named ClassName+CategoryName.
我们一般将categories 放在自己的文件中,一般命名方式是ClassName+CategoryName.
So, in our case, this would be NSString+NumberConvenience. This isn't a requirement, but it's a good practice.
1.2 Let's create a category
(1)To create a category, open the project, go to the Navigator, and select the group where you want the files to show up. Next, select File New New File or type ⌘N.
1.3 @interface
The declaration of a category looks a lot like the declaration for a class:
category 的声明和一个类的声明很像 。
@interface NSString (NumberConvenience)
- (NSNumber *) lengthAsNumber;
@end // NumberConvenience
@interface 的含义
"We're adding a category onto NSString called NumberConvenience."
You can also add properties to categories. Because you can't add instance variables, the properties must be of @dynamic type.
你也可以往cagegories添加属性,因为你不能添加实例变量,因此properties 必须是@dynamci 类型。
The benefit of adding properties is that you can use dot notation to access the setters and getters.
添加属性的好处是你能用dot notation来获取setter 和getter 方法。
1.4 @implementation
@implementation NSString (NumberConvenience)
- (NSNumber *) lengthAsNumber
{
NSUInteger length = [self length];
return ([NSNumber numberWithUnsignedInt:length]);
} // lengthAsNumber
@end // NumberConvenience
[dict setObject:[@"hello" lengthAsNumber]
forKey:@"hello"];
[dict setObject:[@"iLikeFish" lengthAsNumber]
forKey:@"iLikeFish"];
[dict setObject:[@"Once upon a time" lengthAsNumber]
forKey:@"Once upon a time"];
1.5Bad categories
Categories have two limitations. The first is that you can't add new instance variables to a class. There's nowhere to put them.
categories 有两个限制。第一是你不能往一个类中添加新的实例变量。
The second limitation concerns name collisions, in which one of your category methods has the same name as an existing method. When names collide, the category wins. Your category method will completely replace the original method, with no way of getting the original back.
第二是你的category方法可能和现有类中得方法冲突,那么这个category 的方法就会完全取代类方法。
1.6Good categories
categories are used mainly for three purposes: splitting a class's implementation across multiple files or multiple frameworks, creating forward references for private methods, and adding informal protocols to an object.
有三个主要用途:
(1)通过多个框架多个文件把一个类的实现分开。
(2)为private 方法创建前端引用。
(3)为一个对象添加一个非正式协议。
1.7 类扩展 class extensions
class extensions 是一个特别的category.
This special class extension category is not named.
这个特别的class extension category 没有被命名。
(1)As we've pointed out, it doesn't have a name.没有被命名
(2)You can use it for classes you have the source for (that is, your own classes).
你可以用它到有源码的地方。也就是你自己的类。
(3)You can add instance variables.
你可以添加实例变量
(4)You can change the mutability of variables from readonly to readwrite.
你可以将不可变的实例变量从只读到可读写。
(5)You can have any number of them.
你可以有任意数量的他们。
@interface Things : NSObject
@property (assign) NSInteger thing1;
@property (readonly, assign) NSInteger thing2;
- (void)resetAllValues;
@end
The class has two properties and a method. We have marked the thing2 property as readonly, and that's all that's vsisible in the class's public interface. But there's more to it than that, as we'll see when we take a peek at the implementation.
我们声明了两个实例变量并标注thing2只读。
@interface Things ()
{
NSInteger thing4;
}
@property (readwrite, assign) NSInteger thing2;
@property (assign) NSInteger thing3;
@end
在implementation里我们声明里一个
This looks almost like we're defining a class, except that there's no parent class. What we're doing is basically taking the class Things and extending it by adding private properties and methods. That's why it's called class extension.
开起来好像是声明了一个没有父类的类。我们所做的仅仅是获取Thing这个类,并添加一些私有的方法和属性。这也是为什么叫他类扩展。
Looking at the thing2 property,We're changing its mutability, marking it readwrite so that the compiler will generate a setter but keep access private to this class.The public interface has only a getter.
things2这个属性,我们改变了他得不可变性,让他可读写。所以编译器将产生setter方法,但是仅能在这个类中有。公共interface 只有一个getter 方法。
And we added an instance variable named thing4, which is also private.
我们添加了一个实例变量thing4,它仍然是个私有方法。
So why would you want to do something like this? One of the characteristics of object-oriented programming is information hiding. You want to expose only things that users need to see, and nothing else, such as implementation details.
为什么这么做?面向对象的一个特征就是信息隐藏。你只需要暴露给用户想看见的东西。
We put this category in the .m file, but we could put it in the private header (ThingsPrivate.h) and allow Things subclasses or friend classes to have access to these items.
我们把category 放进.m 文件中,我们也可以放到private header 中。并允许Things的子类和friend classes 获取这些。
1.9 Splitting an Implementation with Categories
If you have a single large class you want to split across multiple .m files, you can use categories to do the job.
如果你想把一个大类分成多个.m 文件,你可以用categories.
for instance, the NSWindow,you'll find hundreds of methods;
你能找到很多方法。
Putting all the code for NSWindow into one file would make it huge and unwieldy
把它们放在一个文件显然不合适。
@interface NSWindow : NSResponder
Then, there are a whole bunch of categories, including these:
@interface NSWindow(NSKeyboardUI)
@interface NSWindow(NSToolbarSupport)
@interface NSWindow(NSDrag)
@interface NSWindow(NSCarbonExtensions)
This use of categories allows all the keyboard user interface stuff to live in one source file, the toolbar code in another file, drag-and-drop features in yet another, and so on.
These categories also break the methods into logical groups, making life easier for folks who are reading the header file. That's what we're going to try but on a smaller scale.
这些categories 使得方法按照逻辑分组,使得那些读头文件的人理解起来更容易。
When you're using an object, whether the methods are declared in the interface, in a superclass, or in a category doesn't matter.
当你使用一个对象的时候,不管他的方法声明在interface,superclass,还是category都无所谓。
Not only can you split a class's implementation across multiple source files, you can divide it among multiple frameworks as well.
你不仅可以将class的实现放到不同的源文件中,你也可以把他们放到不同框架中。
1.10 categories to rescue
Declaring a method in a category is enough for the compiler to say, "OK, this method exists. I'm not going to complain if I see the programmer using it." You don't actually have to implement it if you don't want to.
在一个category 中声明一个方法,就足够告诉编译器了:"这个方法应尽存在,如果我卡到程序用到这个方法,我将不会再抱怨了"。
One common technique is to place a category at the top of the implementation file.
一种通用的方法是把一个category 放在实现类的顶部。
1.11 Informal Protocols and Delegation Categories 非正式协议和委托 categories
Cocoa classes often use a technique that involves a delegate, which is an object asked by another object to do some of its work.
cocoa 经常会用到一项成为委托的技术。也就是说一个对象被另外一个对象要求做一些工作。
For example, the AppKit class NSApplication asks its delegate if it should open an Untitled window when the application launches. NSWindow objects ask their delegates if they should allow a window to be closed
例如,当应用启动时,一个appkit 对象NSApplication 询问他的委托 ,NSapplication是否应该打开一个未命名的窗口。NSwindow对象要询问他们的委托,NSWindwo 是否应该允许一个窗口被关掉。
Most often, you will be the one writing the delegate object and giving it to some other object, typically something provided by Cocoa. By implementing specific methods, you can exert control over how the Cocoa object behaves.
更一般情况下,你将写一些委托对象,你把它给其他对象,一般是cocoa提供的。通过实现特殊的方法,你能利用控制cocao 对象的表现。
Scrolling lists in Cocoa are handled by the AppKit class NSTableView. When the tableView is ready to do some work, such as selecting the row the user just clicked, the object asks its delegate if it can select the row.
在cocoa 中得scrolling 列表通过Appkit 类NSTableView 被控制。当tableView 准备好做工作的时候,比如选择用户刚刚点击的对象,tableView 将会发送一个委托看看是不能选择此列。
The tableView sends a message to its delegate:
- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (NSInteger) rowIndex;
tableView将会发送一个协议,就是- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (NSInteger) rowIndex;
The delegate method can look at the tableView and the row to decide whether the row should be selected. If the table includes rows that shouldn't be selected, the delegate might implement the concept of disabled rows that are not selectable.
这个delegate方法可能查找tableView和那个能决定是否应该被选择的row。如果这个table 包含了那个不应该被选择的列,那么delegate将实现不让选择行的概念。
1.12 project
The Cocoa class that lets you find network services published by Bonjour (the technology formerly called Rendezvous) is named NSNetServiceBrowser.
允许你通过发布的Bonjour找到网络服务的类名字为NSNetServiceBrowser.
You tell the net service browser what service you're looking for and give it a delegate object.
你告诉net service browser 你要寻找什么服务,并把它告诉给一个委托对象。
The browser object then sends messages to the delegate object telling it when it sees new services.
browser 对象将发送一条信息给委托对象,告诉委托对象什么时候能看到新的服务。
ITunesFinder, which lives in the 12.04 ITunesFinder project folder, uses NSNetServiceBrowser to list all the shared iTunes music libraries that it can find.
ITunesFinder利用NSNetServiceBrowser列出所有的分享的iTunes music libraries
a new NSNetServiceBrowser is born:
NSNetServiceBrowser *browser = [[NSNetServiceBrowser alloc] init];
then a new ITunesFinder is created:
ITunesFinder *finder = [[ITunesFinder alloc] init];
we tell the net service browser to use the ITunesFinder object as a delegate: [browser setDelegate: finder];
we tell the browser to go look for iTunes shares:
[browser searchForServicesOfType: @"_daap._tcp" inDomain: @"local."];
The "_daap._tcp" string tells the net service browser to look for services of type daap ("daap" is short for "Digital Audio Access Protocol") using the TCP networking protocol.
_daap._tcp告诉net service browser 来寻找类型为daap(daap si short for digital audio access protocol ) 通过tcp networking protocol .
The domain local. means to look for the services on the local network.
local 意味着将在本地网络中寻找服务。
main() logs the fact that it has begun browsing and starts a run loop: NSLog (@"begun browsing");
[[NSRunLoop currentRunLoop] run];
A run loop is a Cocoa construct that blocks (that is, doesn't do any processing) until something interesting happens. In this case, "interesting" means that the net service browser discovers a new iTunes share.
一个run loop 是一个直到感兴趣的东西发生之前cocoa构造的 block(不做任何事情)。
In addition to listening for network traffic, run loops handle other things like waiting for user events, such as key presses or mouse clicks.
除了监听网络状况之外,run loop 还处理一些比如用户事件,想键盘输入或鼠标点击的事情。
The run method actually will not return; it will keep running forever, so the code that follows it won't ever execute.
这个run 方法实际上不会返回。它将永远运行。
#import <Foundation/Foundation.h>
@interface ITunesFinder : NSObject <NSNetServiceBrowserDelegate>
@end // ITunesFinder
The implementation has two methods. First come the preliminaries:
#import "ITunesFinder.h"
@implementation ITunesFinder
and then the first delegate method:
- (void) netServiceBrowser: (NSNetServiceBrowser *) b
didFindService: (NSNetService *) service
moreComing: (BOOL) moreComing {
[service resolveWithTimeout: 10];
NSLog (@"found one! Name is %@",
[service name]);
} // didFindService
When an NSNetServiceBrowser finds a new service, it sends the netServiceBrowser:didFindSer vice:moreComing: message to the delegate object.
当NSNetServiceBrowser 找到一个新的服务时,他将把netServiceBrowser:didFindSer vice:moreComing: message发送给委托对象。
The browser is passed as the first argument (which would be the same as the value of the browser variable in main).
browser 是第一个穿过来的参数。(browser 将和main 中的browser variable 一样)
f you have multiple service browsers doing searches at the same time, examining this parameter lets you figure out which one has found something.
如果你有多个service browsers 来同时做searches ,检查这个参数将能让你知道到底是那个找到了东西。
The NSNetService object passed in the second argument is an object that describes the service that was found, such as an iTunes share.
NSNetService 是被传递过来的第二个参数。它将表述什么服务被发现了。
The last argument, moreComing, is used to signal when a batch of notifications is done.
最后一个参数 被用来表明 一系列的notification已经找到了。
Why did the Cocoa designers include this moreComing parameter?
为什么cocoa 要包含这个参数?
If you ran this program on a big college network with a hundred iTunes shares, this method would get called 99 times with moreComing having the value YES and then once with a value of NO. This information is handy to have when constructing the user interface, so you know when to update your window. As new iTunes shares come and go, this method is called again and again.
如果你运行这个项目在一个很大校园网里,有一百多个音乐分享,这个方法将得到99次YES,为moreComing的参数,而一次 NO。当创建一个用户接口的时候,这个信息很好用。当一个iTunes shares 来和去,这个方法都将运行。
[service resolveWithTimeout: 10] tells the Bonjour system to go fetch all the interesting properties about the service.
告诉Bonjour系统去获取所有关于这个服务有兴趣的属性。
In particular, we want the name of the share, like Scott's Groovy Tunes, so we can print it out. [service name] gets us the name of the share.
特别的我们想获得servcie 的名字信息。
iTunes shares can come and go, as people put their laptops to sleep or move off the network. The ITunesFinder class implements a second delegate method that gets called when a network service vanishes:
iTunes shares 能够来和去,例如人们关闭计算机或者关掉了网络。
ITunesFinder Class 实现了第二个委托方法:当一个网络关掉的时候。
- (void) netServiceBrowser: (NSNetServiceBrowser *) b
didRemoveService: (NSNetService *) service
moreComing: (BOOL) moreComing
{
[service resolveWithTimeout: 10];
NSLog (@"lost one! Name is %@",
[service name]);
} // didRemoveService
1.13 Delegate and categories
Delegates highlight another use of categories: the methods that can be sent to a delegate are declared as a category on NSObject.
delegate让categories 有了其他的用处:能够被发送的delegate能够声明为一个NSObject的category。
@interface NSObject (NSNetServiceBrowserDelegateMethods)
- (void) netServiceBrowserWillSearch:(NSNetServiceBrowser *) browser;
- (void) netServiceBrowser:(NSNetServiceBrowser *) aNetServiceBrowserÉ
didFindService:(NSNetService *) service moreComing: (BOOL) moreComing;
- (void) netServiceBrowserDidStopSearch:(NSNetServiceBrowser *) browser;
- (void) netServiceBrowser:(NSNetServiceBrowser *) browser didRemoveService:É
(NSNetService *) service moreComing: (BOOL) moreComing;
@end
By declaring these methods as a category on NSObject, the implementation of NSNetServiceBrowser can send one of these messages to any object, no matter what class it actually is.
通过声明这些方法为NSObject的一个category,NSNetServiceBrowser的实现能够向任意一个对象发送这些消息。
This also means that any kind of object can be a delegate, as long as it implements the method.
也就是说任意种类的对象都能够成为一个delegate,只要他们实现了这些方法。
Putting a category on NSObject is called creating an informal protocol.
在一个category放在NSObject中被称为非正式协议。
As you know, a "protocol" in computer-speak is a set of rules that govern communication.
一个协议就是说官方通信的一系列规则。
An informal protocol is simply a way to say, "Here are some methods you might want to implement so you can do cool stuff with them."
非正式协议就是说:这里有一些你想实现的方法,这样就能做一些很cool的事情了。
1.14 responds to selectors
What is a selector? It's just the name of a method, but it's encoded in a special way that's used by the Objective-C runtime for quick lookups.
什么是个selector?它仅仅是个方法的名字,但是它为了快速回路 ,以一种通过Objective-C 运行时的特殊的方式编码。
You indicate a selector by using the @selector() compiler directive, with the name of the method nestled in the parentheses.
你可以证明一个selector通过@selector直接编,以及括号里地方法。
the selector for the Car method setEngine: would be
@selector(setEngine:)
And this would be the selector for the setTire:atIndex: Car method:
@selector(setTire:atIndex:)
NSObject provides a method called respondsToSelector: that queries an object to see if it will respond to a given message.
NSObject 提供了一个成为respondsToSelector的方法。它用来查询一个对象是否相应一个给定的消息。
The following chunk of code uses respondsToSelector:
Car *car = [[Car alloc] init];
if ([car respondsToSelector: @selector(setEngine:)])
{
NSLog (@"yowza!");
}
To find out what it needs to know, NSNetServiceBrowser would call respondsToSelector:@selector(netServiceBrowser:didFindService:moreComing:). If the delegate can respond to that message, the browser will send the message. Otherwise, the browser ignores that delegate for now and just goes on its merry way.
为了找到需要知道什么,NSNetServiceBrowser将调用respondsToSelector:@sel ector(netServiceBrowser:didFindService:moreComing:)..如果委托相应了这个效益,那么browser将发送消息。否则browser 将忽略这个委托