Protocols and Categories

1. Delegate的调用

// EOCSoundPlayer.h
#import <Foundation/Foundation.h>

@class EOCSoundPlayer;
@protocol EOCSoundPlayerDelegate <NSObject>
- (void)soundPlayerDidFinish:(EOCSoundPlayer*)player;
@end


@interface EOCSoundPlayer : NSObject
@property (nonatomic, weak) id <EOCSoundPlayerDelegate> delegate; - (id)initWithURL:(NSURL*)url;
- (void)playSound;
@end

// EOCSoundPlayer.m
#import "EOCSoundPlayer.h"
#import <AudioToolbox/AudioToolbox.h>

void completion(SystemSoundID ssID, void *clientData)
{
    EOCSoundPlayer *player =
    (__bridge EOCSoundPlayer*)clientData;
    if ([player.delegate
    respondsToSelector:@selector(soundPlayerDidFinish:)])
    {
        [player.delegate soundPlayerDidFinish:player];
    }
}

@implementation EOCSoundPlayer
{
    SystemSoundID _systemSoundID;
}

- (id)initWithURL:(NSURL*)url
{
    if ((self = [super init]))
    {
        AudioServicesCreateSystemSoundID((__bridge  CFURLRef)url,     &_systemSoundID);
    }
    return self;
}

- (void)dealloc
{
    AudioServicesDisposeSystemSoundID(_systemSoundID);
}

- (void)playSound
{
    AudioServicesAddSystemSoundCompletion(
    _systemSoundID, NULL,
    NULL,
    completion, (__bridge void*)self);
    AudioServicesPlaySystemSound(_systemSoundID);
}
@end

2. Use Categories to Break Class Implementations into Manageable Segments

方法的分类:

  • Consider a class that models a person. The person might have a few methods available on it:
#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;

/* Friendship methods */
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;

/* Work methods */
- (void)performDaysWork;
- (void)takeVacationFromWork;

/* Play methods */
- (void)goToTheCinema; - (void)goToSportsGame;

@end
  • The implementation for such a class would contain all methods in a long list in one big file. As more methods are added to the class, the file will only get longer and more unmanageable. So it is often useful to split up such a class into separate, distinct portions. For example, the preceding could be rewritten to make use of categories:
#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSArray *friends;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;
@end

@interface EOCPerson (Friendship)
- (void)addFriend:(EOCPerson*)person;
- (void)removeFriend:(EOCPerson*)person;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end

@interface EOCPerson (Work)
- (void)performDaysWork;
- (void)takeVacationFromWork;
@end

@interface EOCPerson (Play)
- (void)goToTheCinema;
- (void)goToSportsGame;
@end

Now, each distinct part of the class is split into separate categories of methods. Not surprisingly, this language feature is called categories! In the example, the bases of the class, including the properties and initializer, are declared within the main implementation. Additional sets of methods, relating to different types of actions, are split into categories.

3. Always Prefix Category Names on Third-Party Classes

如果你没有加前缀,假如系统的类库也有这个方法那么你写的扩展方法就会出错:

@interface NSString (HTTP)
// Encode a string with URL encoding
- (NSString*)urlEncodedString;

// Decode a URL encoded string
- (NSString*)urlDecodedString;
@end
warning: duplicate definition of category 'HTTP' on interface 'NSString'

应该像下面这样写:

@interface NSString (ABC_HTTP)
// Encode a string with URL encoding
- (NSString*)abc_urlEncodedString;

// Decode a URL encoded string
- (NSString*)abc_urlDecodedString;@end

4. Avoid Properties in Categories

A property is a way of encapsulating data . Although it is technically possible to declare a property in a category, you should avoid doing so if possible. The reason is warning: duplicate definition of category 'HTTP' on interface 'NSString' that it is impossible for a category, except the class-continuation category, to add instance variables to a class. Therefore, it is also impossible for the category to synthesize an instance variable to back the property.

#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;

- (id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;
@end

@implementation EOCPerson
// Methods
@end

@interface EOCPerson (Friendship)
@property (nonatomic, strong) NSArray *friends;
- (BOOL)isFriendsWith:(EOCPerson*)person;
@end

@implementation EOCPerson (Friendship)
// Methods
@end

If you compile this, however, you would end up with compiler warnings:

warning: property 'friends' requires method 'friends' to be defined - use @dynamic or provide a method implementation in this category [-Wobjc-property-implementation]
warning: property 'friends' requires method 'setFriends:' to be defined - use @dynamic or provide a method implementation in this category [-Wobjc-property-implementation]

5. Use the Class-Continuation Category to Hide Implementation Detail

只有在实现文件里面匿名的扩展类里可以声明属性,当你不想在接口里暴露这个属性时:

@interface EOCPerson ()

@property (nonatomic, copy, readwrite) NSString *_anotherInstanceVariable;

// Method declarations here
@end

@implementation EOCPerson
{
    int _anotherInstanceVariable;
}
// Method implementations here@end