iOS subclassing

In order to avoid tearing, I declare in advance: This article is purely translation, just for learning, plus the level is limited, forgive me!

【原文】https://www.objc.io/issues/13-architecture/singletons/

iOS Subclassing - by Chris Eidhof

This article is a little different from what I usually write. It's less of a guide and more of a collection of ideas and patterns. Almost all the patterns I'm about to describe are found the hard way by making mistakes. By no means I'm an authority on subclassing, but I just wanted to share some of the things I've learned. Don't use it as a definitive guide, but a collection of examples.

When asked about object-oriented programming ( OOP), Alan Kay (creator) writes that he's not about classes, but message passing ^ ?. However, there is still a lot of focus on creating class hierarchies. In this post, we'll look at a few useful cases, but we'll mostly focus on creating complex class hierarchies. In our experience, this makes the code simpler and easier to maintain. There are many articles on this topic, which you can find in books like Clean Code and Code Complete , which I recommend everyone to read.

When to subclass

First, let's discuss when it makes sense to subclass. If you are building one with a custom layout UITableViewCellthen you need to create a subclass. This is true of almost every point; once you start laying out, it makes sense to move it into a subclass so that you not only neatly organize your code, but also make the code reusable throughout the project.

Suppose, your code is for different platforms and versions, and you need to write custom parts for each platform and version. This is where it makes sense to create a OBJDeviceclass that can be subclassed out OBJIphoneDeviceand OBJIPadDevice, even deeper OBJIPhone5Device, that can override specific methods. For example, your OBJDeviceclass can contain applyRoundedCornersToView:withRadius:methods. It has a default implementation, but this method can be overridden by specific subclasses.

Another very useful case of subclassing is in model objects ( model object). Most of the time, the implementation methods that model objects inherit from classes are isEqual:, hash, copyWithZone:and description. These methods are implemented once by iterating over the properties and it is hard to go wrong. (If you're looking for such a base class, consider using Mantle, which does exactly that, and more than that.)

When not to subclass

I've worked on a lot of projects and I've seen deep subclassing. I'm ashamed that I did that too. Unless the layers are very shallow, the limit is quickly reached.

Fortunately, if you find yourself at such a deep level, there are plenty of options. In the following subsections, we will analyze each part in depth. This is a good option if your subclasses just share the same interface and protocol. If you know that an object needs to be modified a lot, you may need to use delegates to dynamically change and configure it. categoryCategory( ) may be an option when you want to extend some simple functionality to an existing object . When you have a series of subclasses, each of which override( override) the same method, you might use a configuration object. Finally, when you want to reuse some functionality, it's better to combine multiple objects rather than extend them.

Alternative

Scheme: Protocol

Often, one reason to use subclassing is when you want to ensure that an object responds to a certain message. Imagine that in an application you have a Player( player) object that can play a video. Now, if you want to add YouTobesupport, you need the same interface, but a different implementation. One is to implement it with subclasses as follows:

@interface Player: NSObject
- (void)play;
- (void)pause;
@end

@interface YouTobePlayer: Player
@end

Maybe, the two classes don't share much code, just the same interface. In this case, using a protocol might be a better solution. Using protocols, you might write code like this:

@protocol VideoPlayer <NSObject>
- (void)play;
- (void)pause;
@end

@interface Player: NSObjet <VideoPlayer>
@end

@interface YouTobePlayer: NSObject <VideoPlayer>
@end

In this case, there is YouTobePlayerno need to know Playerthe internals.

Scheme: Delegate

Suppose again, that you have a Playerclass like the example above. Now, in one place, you want to palyperform a custom action in a method. It's fairly easy to do: you can create a custom subclass, override the palymethod, call [super paly]it, and perform custom functionality. This is one way of handling it. Another is to mutate the Playerobject and give him a delegate. E.g:

@class Player;
@protocol PlayerDelegate
- (void)playerDidStartPlaying:(Player *)player;
@end

@interface Player: NSObject
@property (nonatomic, weak) id<PlayerDelegate> delegate;
- (void)play;
- (void)pause;
@end

Now, in the player's playmethod, the delegate gets the playerDidStartPlaying:message. Any user of this class simply implements the delegation protocol without subclassing, and Playerthe object remains generic. This is a very powerful technique that Apple uses heavily in their own frameworks. Sometimes, you want to organize different methods into different protocols, as UITableView does, which not only has delegates but also data sources.

Scheme: Category

Sometimes, you may want to extend the functionality of an object a little. arrayByRemovingFirstObjectSuppose, you want to extend a method to NSArray . You can put it into a class instead of creating a subclass. like this:

@interface NSArray (OBJExtras)
- (void)obj_arrayByRemovingFirstObject;
@end

When extending a class that you didn't create yourself with a class, it's a good practice to prefix the method. If you don't, someone else might implement the same method using the same technique. Then, if the behavior doesn't match, unexpected things can happen.

One of the dangers of using categories is that your project may end up using a lot of categories, and you can lose your overview. In this case, it might be simpler to create a custom class.

Scenario: Configuration Object

One mistake I've been making (now quickly realized) is that a class is created with a lot of abstract methods, and then a lot of subclasses override a specific method. For example, in a presentation application, you might have a Themeclass that has several properties like : backgroundColorand font, and some logic for placing things on the slides.

Then, for each theme, you create a Themesubclass, override a method (eg: setup), and configure the properties. There is no point in using subclasses directly. In this case, you can use configuration objects to make your code simpler. You can put shared logic in a Themeclass (eg: slide layout), but put the configuration in a simple object with only properties.

For example, a ThemeConfigurationclass has backgroundColorand fontproperties, and Themethe class gets an instance of this class when it is initialized.

Scheme: Combination

The most powerful alternative to subclassing is composition. If you want to reuse existing code but don't share the same interface, composition is your "weapon" of choice. For example, suppose you are designing a cache class:

@interface OBJCache: NSObject
- (void)cacheValue:(id)value forKey:(NSString *)key;
- (void)removeCacheValueForKey:(NSString *)key;
@end

An easy way to do this is to subclass NSDictionaryand implement both methods by calling the dictionary methods.

@interface OBJCache: NSDictionary

However, this has several disadvantages. The fact that it is implemented with a dictionary should be an implementation detail. Now, anywhere a NSDictionarytype parameter is required, you can provide a OBJCachevalue. If you want to switch to something completely different (for example, your own class library), you need to refactor a lot of code.

A good way is to store this dictionary in a private property (or instance variable) and expose only two cachemethods. Now, you can maintain the flexibility to modify the implementation at will as you gain more insights, and consumers of the class don't need to refactor.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325377236&siteId=291194637
ios