It’s the job of a regular Swift initializer to create a fully fledged instance of a new type, however sometimes the data that has been provided is insufficient or incorrect, and creation can’t proceed.
For example, consider this code:
structPerson{var ssn:Stringinit(socialSecurityNumber:String){self.ssn = socialSecurityNumber
}}let person =Person(socialSecurityNumber:"111-11-1111")print(person)
That defines a Person struct that can be created using a nine-digit social security number, then creates an instance of that struct.
But what should happen here?
let person =Person(socialSecurityNumber:"FISH")
In that instance we’re passing an invalid social security number, so really we expect creating a Person to fail.
This is where failable initializers come in: they are written as init?(), and can return nil rather than a value if something goes wrong during creation. For example, we could write a quick check to make sure the social security number is more or less correct like this:
Notice the initializer is now called init?() to reflect that it returns an optional – the process might return nil if the creation fails. The logic is pretty simple: if there are 11 digits we assume it’s correct, otherwise we return nil. Note: if you really wanted to validate that number you’d need to use a regular expression.
Categories are an Objective-C language feature that let you add new methods to an existing class, much like C# extensions. However, do not confuse C# extensions with Objective-C extensions. Objective-C’s extensions are a special case of categories that let you define methods that must be declared in the main implementation block.
These are powerful features that have many potential uses. First, categories make it possible to split up a class’ interface and implementation into several files, which provides much-needed modularity for larger projects. Second, categories let you fix bugs in an existing class (e.g., NSString) without the need to subclass it. Third, they provide an effective alternative to the protected and private methods found in C# and other Simula-like languages.
Categories
A category is a group of related methods for a class, and all of the methods defined in a category are available through the class as if they were defined in the main interface file. As an example, take the Person class that we’ve been working with throughout this book. If this were a large project, Person may have dozens of methods ranging from basic behaviors to interactions with other people to identity checking. The API might call for all of these methods to be available through a single class, but it’s much easier for developers to maintain if each group is stored in a separate file. In addition, categories eliminate the need to recompile the entire class every time you change a single method, which can be a time-saver for very large projects.
Let’s take a look at how categories can be used to achieve this. We start with a normal class interface and a corresponding implementation:
// Person.h
@interfacePerson: NSObject
@interfacePerson: NSObject
@property(readonly) NSMutableArray* friends;
@property(copy) NSString* name;
- (void)sayHello;
- (void)sayGoodbye;
@end
// Person.m
#import "Person.h"
@implementationPerson
@synthesizename = _name;
@synthesizefriends = _friends;
-(id)init{
self= [superinit];
if(self){
_friends = [[NSMutableArrayalloc]init];
}
returnself;
}
- (void)sayHello {
NSLog(@"Hello, says %@.", _name);
}
- (void)sayGoodbye {
NSLog(@"Goodbye, says %@.", _name);
}
@end
Nothing new here-just a Person class with two properties (the friends property will be used by our category) and two methods. Next, we’ll use a category to store some methods for interacting with other Person instances. Create a new file, but instead of a class, use the Objective-C Category template. Use Relations for the category name and Person for the Category on field:
Creating the Person+Relations class
As expected, this will create two files: a header to hold the interface and an implementation. However, these will both look slightly different than what we’ve been working with. First, let’s take a look at the interface:
// Person+Relations.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interfacePerson(Relations)
- (void)addFriend:(Person*)aFriend;
- (void)removeFriend:(Person*)aFriend;
- (void)sayHelloToFriends;
@end
Instead of the normal @interface declaration, we include the category name in parentheses after the class name we’re extending. A category name can be anything, as long as it doesn’t conflict with other categories for the same class. A category’s file name should be the class name followed by a plus sign, followed by the name of the category (e.g., Person+Relations.h ).
So, this defines our category’s interface. Any methods we add in here will be added to the original Person class at run time. It will appear as though the addFriend:, removeFriend:, and sayHelloToFriends methods are all defined in Person.h, but we can keep our functionality encapsulated and maintainable. Also note that you must import the header for the original class, Person.h. The category implementation follows a similar pattern:
// Person+Relations.m
#import "Person+Relations.h"
@implementationPerson(Relations)
- (void)addFriend:(Person*)aFriend {
[[selffriends]addObject:aFriend];
}
- (void)removeFriend:(Person*)aFriend {
[[selffriends]removeObject:aFriend];
}
- (void)sayHelloToFriends {
for(Person*friend in[selffriends]) {
NSLog(@"Hello there, %@!", [friendname]);
}
}
@end
This implements all of the methods in Person+Relations.h. Just like the category’s interface, the category name appears in parentheses after the class name. The category name in the implementation should match the one in the interface.
Also, note that there is no way to define additional properties or instance variables in a category. Categories have to refer back to data stored in the main class (friends in this instance).
It’s also possible to override the implementation contained in Person.m by simply redefining the method in Person+Relations.m. This can be used to monkey patch an existing class; however, it’s not recommended if you have an alternative solution to the problem, since there would be no way to override the implementation defined by the category. That is to say, unlike the class hierarchy, categories are a flat organizational structure-if you implement the same method in two separate categories, it’s impossible for the runtime to figure out which one to use.
The only change you have to make to use a category is to import the category’s header file. As you can see in the following example, the Person class has access to the methods defined in Person.h along with those defined in the category Person+Relations.h:
// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+Relations.h"
intmain(intargc, constchar* argv[]) {
@autoreleasepool{
Person*joe = [[Personalloc]init];
joe.name= @"Joe";
Person*bill = [[Personalloc]init];
bill.name= @"Bill";
Person*mary = [[Personalloc]init];
mary.name= @"Mary";
[joesayHello];
[joeaddFriend:bill];
[joeaddFriend:mary];
[joesayHelloToFriends];
}
return0;
}
And that’s all there is to creating categories in Objective-C.
Protected Methods
To reiterate, all Objective-C methods are public-there is no language construct to mark them as either private or protected. Instead of using “true” protected methods, Objective-C programs can combine categories with the interface/implementation paradigm to achieve the same result.
The idea is simple: declare “protected” methods as a category in a separate header file. This gives subclasses the ability to “opt-in” to the protected methods while unrelated classes use the “public” header file as usual. For example, take a standard Ship interface:
// Ship.h
#import <Foundation/Foundation.h>
@interfaceShip: NSObject
- (void)shoot;
@end
As we’ve seen many times, this defines a public method called shoot. To declare a protected method, we need to create a Ship category in a dedicated header file:
// Ship_Protected.h
#import <Foundation/Foundation.h>
@interfaceShip(Protected)
- (void)prepareToShoot;
@end
Any classes that need access to the protected methods (namely, the superclass and any subclasses) can simply import Ship_Protected.h. For example, the Ship implementation should define a default behavior for the protected method:
// Ship.m
#import "Ship.h"
#import "Ship_Protected.h"
@implementationShip{
BOOL_gunIsReady;
}
- (void)shoot {
if(!_gunIsReady) {
[selfprepareToShoot];
_gunIsReady = YES;
}
NSLog(@"Firing!");
}
- (void)prepareToShoot {
// Execute some private functionality.
NSLog(@"Preparing the main weapon...");
}
@end
Note that if we hadn’t imported Ship_Protected.h, this prepareToShoot implementation would be a private method, as discussed in the Methods chapter. Without a protected category, there would be no way for subclasses to access this method. Let’s subclass the Ship to see how this works. We’ll call it ResearchShip:
// ResearchShip.h
#import "Ship.h"
@interfaceResearchShip: Ship
- (void)extendTelescope;
@end
This is a normal subclass interface-it should not import the protected header, as this would make the protected methods available to anyone that imports ResearchShip.h, which is precisely what we’re trying to avoid. Finally, the implementation for the subclass imports the protected methods and (optionally) overrides them:
// ResearchShip.m
#import "ResearchShip.h"
#import "Ship_Protected.h"
@implementationResearchShip
- (void)extendTelescope {
NSLog(@"Extending the telescope");
}
// Override protected method
- (void)prepareToShoot {
NSLog(@"Oh shoot! We need to find some weapons!");
}
@end
To enforce the protected status of the methods in Ship_Protected.h, other classes aren’t allowed to import it. They’ll just import the normal “public” interfaces of the superclass and subclass:
// main.m
#import <Foundation/Foundation.h>
#import "Ship.h"
#import "ResearchShip.h"
intmain(intargc, constchar* argv[]) {
@autoreleasepool{
Ship*genericShip = [[Shipalloc]init];
[genericShipshoot];
Ship*discoveryOne = [[ResearchShipalloc]init];
[discoveryOneshoot];
}
return0;
}
Since neither main.m, Ship.h, nor ResearchShip.h import the protected methods, this code won’t have access to them. Try adding a [discoveryOne prepareToShoot] method-it will throw a compiler error, since the prepareToShoot declaration is nowhere to be found.
To summarize, protected methods can be emulated by placing them in a dedicated header file and importing that header file into the implementation files that require access to the protected methods. No other files should import the protected header.
While the workflow presented here is a completely valid organizational tool, keep in mind that Objective-C was never meant to support protected methods. Think of this as an alternative way to structure an Objective-C method, rather than a direct replacement for C#/Simula-style protected methods. It’s often better to look for another way to structure your classes rather than forcing your Objective-C code to act like a C# program.
Caveats
One of the biggest issues with categories is that you can’t reliably override methods defined in categories for the same class. For example, if you defined an addFriend: class in Person(Relations) and later decided to change the addFriend: implementation via a Person(Security) category, there is no way for the runtime to know which method it should use since categories are, by definition, a flat organizational structure. For these kinds of cases, you need to revert to the traditional subclassing paradigm.
Also, it’s important to note that a category can’t add instance variables. This means you can’t declare new properties in a category, as they could only be synthesized in the main implementation. Additionally, while a category technically does have access to its classes’ instance variables, it’s better practice to access them through their public interface to shield the category from potential changes in the main implementation file.
Extensions
Extensions (also called class extensions) are a special type of category that requires their methods to be defined in the main implementation block for the associated class, as opposed to an implementation defined in a category. This can be used to override publicly declared property attributes. For example, it is sometimes convenient to change a read-only property to a read-write property within a class’ implementation. Consider the normal interface for a Ship class:
Included code sample: Extensions
// Ship.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interfaceShip: NSObject
@property(strong, readonly) Person*captain;
- (id)initWithCaptain:(Person*)captain;
@end
It’s possible to override the @property definition inside of a class extension. This gives you the opportunity to re-declare the property as readwrite in the implementation file. Syntactically, an extension looks like an empty category declaration:
// Ship.m
#import "Ship.h"
// The class extension.
@interfaceShip()
@property(strong, readwrite) Person*captain;
@end
// The standard implementation.
@implementationShip
@synthesizecaptain = _captain;
- (id)initWithCaptain:(Person*)captain {
self= [superinit];
if(self) {
// This WILL work because of the extension.
[selfsetCaptain:captain];
}
returnself;
}
@end
Note the () appended to the class name after the @interface directive. This is what marks it as an extension rather than a normal interface or a category. Any properties or methods that appear in the extension must be declared in the main implementation block for the class. In this case, we aren’t adding any new fields-we’re overriding an existing one. But unlike categories, extensions can add extra instance variables to a class, which is why we’re able to declare properties in a class extension but not a category.
Because we re-declared the captain property with a readwrite attribute, the initWithCaptain: method can use the setCaptain: accessor on itself. If you were to delete the extension, the property would return to its read-only status and the compiler would complain. Clients using the Ship class aren’t supposed to import the implementation file, so the captain property will remain read-only.
// This will NOT work because the property is still read-only.
[discoveryOnesetCaptain:dave];
}
return0;
}
Private Methods
Another common use case for extensions is for declaring private methods. In the previous chapter, we saw how private methods can be declared by simply adding them anywhere in the implementation file. But, prior to Xcode 4.3, this was not the case. The canonical way to create a private method was to forward-declare it using a class extension. Let’s take a look at this by slightly altering the Ship header from the previous example:
// Ship.h
#import <Foundation/Foundation.h>
@interfaceShip: NSObject
- (void)shoot;
@end
Next, we’re going to recreate the example we used when we discussed private methods in the Methods chapter. Instead of simply adding the private prepareToShoot method to the implementation, we need to forward-declare it in a class extension.
// Ship.m
#import "Ship.h"
// The class extension.
@interfaceShip()
- (void)prepareToShoot;
@end
// The rest of the implementation.
@implementationShip{
BOOL_gunIsReady;
}
- (void)shoot {
if(!_gunIsReady) {
[selfprepareToShoot];
_gunIsReady = YES;
}
NSLog(@"Firing!");
}
- (void)prepareToShoot {
// Execute some private functionality.
NSLog(@"Preparing the main weapon...");
}
@end
The compiler ensures the extension methods are implemented in the main implementation block, which is why it functions as a forward-declaration. Yet because the extension is encapsulated in the implementation file, other objects shouldn’t ever know about it, giving us another way to emulate private methods. While newer compilers save you this trouble, it’s still important to understand how class extensions work, as it has been a common way to leverage private methods in Objective-C programs until very recently.
Summary
This chapter covered two of the more unique concepts in the Objective-C programming language: categories and extensions. Categories are a way to extend the API of existing classes, and extensions are a way to add required methods to the API outside of the main interface file. Both of these were initially designed to ease the burden of maintaining large code bases.
The next chapter continues our journey through Objective-C’s organizational structures. We’ll learn how to define a protocol, which is an interface that can be implemented by a variety of classes.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"><head><title></title><metahttp-equiv="Content-Type"content="text/html; charset=utf-8" /></head><body><divclass="comment"><h6>张三:</h6><pclass="para">沙发.</p></div><divclass="comment"><h6>李四:</h6><pclass="para">板凳.</p></div><divclass="comment"><h6>王五:</h6><pclass="para">地板.</p></div></body></html>
开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。 他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上0×10, 终端就换行,遇上0×07, 终端就向人们嘟嘟叫,例好遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0×20以下的字节状态称为”控制码”。他们又把所有的空 格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉 很好,于是大家都把这个方案叫做 ANSI的”Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
正在这时,大天使加百列及时出现了——一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。
unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,unicode包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。
unicode在很长一段时间内无法推广,直到互联网的出现,为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。