IOS Observer design Pattern _ios

Source: Internet
Author: User
Tags unpack

What is the Observer pattern? Let's make an analogy, it's like you're ordering a newspaper. For example, you want to know that the United States recently released some news, you may subscribe to an American weekly, and once the United States has a new story, the United States Weekly will send a magazine, and mailed to you, when you receive the newspaper, then you will be able to understand the latest developments in the United States. In fact, this is the observer pattern, A is interested in B's change, registering as B's observer, notifying a when B changes, informing B that a change has taken place. This is a very typical use of the observer, which I call the Classical observer pattern. There is, of course, an alternative observer model-the generalized observer model.

From a classic perspective, the Observer pattern is a pattern of notification changes, which is generally considered useful only in situations where the object has changed. Subject objects know that there are observers, the setting maintains a queue for the observer, and from a broad perspective, the Observer pattern is a pattern that passes changes in data, a pattern that is used when viewing object properties, and the Subject object is unaware of the observer's presence, more like onlookers. You need to know the status of the Subject object, so even if the subject object doesn't change, the observer may be able to access the subject object. In other words, a generalized observer pattern is a pattern of passing data between different objects.

The observer pattern should be one of the design patterns that are used on a large scale in object-oriented programming. From the perspective of methodology, traditional cognitive theory holds that the world is made up of objects, and we can understand the nature of objects through constant observation and understanding. The whole human cognitive model is based on the behavior of "observing". We learn about the world by constantly interacting with other objects in the world and observing them. Similarly, in the world of the program, every instance we build is accomplished by not constantly interacting with other objects (viewing the state of other objects or changing the state of other objects), and by observing the changes in other instances and responding to them. That is why the observation pattern is presented separately, and the reason for a specific analysis--which in my view is the basic pattern of many other design patterns and a very important design pattern in programming.

Classic Observer mode

The classical observer pattern is considered to be the behavior pattern of the object, also known as the Publish-subscribe (publish/subscribe) mode, the Model-view (Model/view) mode, the source-listener (Source/listener) mode, or the subordinate (dependents) pattern. The classic observer pattern defines a one-to-many dependency that allows multiple observer objects to simultaneously listen to a Subject object. When the subject object changes in state, all the observer objects are notified so that they can automatically update themselves or make corresponding actions. The example cited at the beginning of the article is the application of the typical observer pattern.

And in the development of iOS we may be exposed to the classic observer mode of implementation, there are several: Nsnotificationcenter, KVO, delegate, etc.

Sense Notification method

In the classical observer mode, because the observer perceives the different patterns of subject object, it is divided into two ways: push model and pull model.

Push model

The subject object pushes the viewer to the details of the subject, regardless of whether the observer needs it, and the information that is pushed is usually all or part of the data of the subject object. The push model realizes the decoupling between the observer and the subject object, and there is no excessive dependency between the two. But the push model sends notifications to all observers every time, in a broadcast manner. All observers passively receive notification. When the content of the notification is too high, multiple observers receive it at the same time, which may have a significant impact on the network, memory (sometimes involving IO).

Typical push model implementations in iOS are Nsnotificationcenter and KVO.

Nsnotificationcenter

Nsnotificationcenter is a typical implementation mode of observer mode with dispatch center. With Nsnotificationcenter as the center, the Observer registers with center the change of a Subject object, the subject object broadcasts through the Nsnotificationcenter. This model is a similar implementation of the article starting to publish subscription newspapers in OC. All observation and monitoring actions are registered with the same center, and all changes to the objects are broadcast out of the same center.

Snotificationcenter, like a hub, is at the heart of the entire observer model, dispatching messages between the observer and the listener.

A complete observation process as shown above. Throughout the process, there are several key classes (the introductory order is in order of completion):

Observer observer, generally inherited from NSObject, through Nsnotificationcenter addobserver:selector:name:o The Bject interface is interested in registering a type of notification. Be sure to note at registration that Nsnotificationcenter does not reference the observer to count +1, and that when we release the Observer in the program, we must report it off from center.

-(void) Handlemessage: (nsnotification*) nc{
//parsing message content
nsdictionary* userInfo = [NC userInfo];
}
-(void) Commoninit
{
//registered observer
[[Nsnotificationcenter defaultcenter] addobserver:self selector: @selector (handlemessage:) name:kdztestnotificatonmessage Object:nil];

Inform the center of Nsnotificationcenter, the hub of the notice.
Subject object, observed object, through PostNotificationName:object:userInfo: send a certain type of notification, broadcast changes.

-(void) postMessage
{
[[Nsnotificationcenter Defaultcenter] Postnotificationname: Kdztestnotificatonmessage Object:nil userinfo:@{}];

Notification Object Nsnotification, when a notification comes, center invokes the observer-registered interface to broadcast the notification while passing the Nsnotification object that stores the change.

The Apple version of the implementation of the Notificationcenter let me use a few small problems that are not very cool

In the use of Nsnotificationcenter, from a programming point of view, we often do not only want to achieve functional implementation, but also hope that the coding efficiency and the entire project maintainability good. The implementation of the Nsnotificationcenter-centric observer model offered by Apple has the following drawbacks in maintainability and efficiency:

Each registered place needs to register a function at the same time, which will bring a lot of coding work. Careful analysis can be found, in fact, each of our observers are almost identical to each registration function. This is a disguised form of CTRLCV, a typical ugly and difficult to maintain code.
Each observer's callback function requires an operation to unpack the messages sent by the subject object. The message is parsed and then manipulated from the userinfo through the KeyValue method. Imagine that there are 100 of places in the project, and the actions to understand the package in the previous function in response to changes. And later demand changes need to send a content, will be a maintenance disaster.

When we use the Observer model on a large scale, we often add a sentence to the Dealloc:

[[Nsnotificationcenter Defaultcenter] removeobserver:self]

And in the actual use of the process, it will be found that the performance of the function is relatively low. During the whole start-up process, 10,000 removeobserver operations were performed,

@implementation dzmessage
-(void) dealloc
{
[[Nsnotificationcenter Defaultcenter] removeobserver:self] ;
}
....
for (int i =; I < i++) {
dzmessage* message = [Dzmessage new];
}

The following figure shows that this process consumes 23.4% of the CPU, indicating that the function is still very inefficient.

This is still the case with only one type of message, and if the entire notificationcenter is mixed with multiple message types, then it's probably disastrous for performance.

for (int i = 0; I < 10000 i++) {
dzmessage* message = [Dzmessage new];
[[Nsnotificationcenter Defaultcenter] addobserver:self selector: @selector (handle) name:[@ (i) stringvalue] Object:nil ];
}

After adding a variety of message types, Removeobserver consumes 63.9% of CPU consumption during startup.

And since Apple doesn't provide the source of the center, it's almost impossible to change the center.

Improved version of the Central Observer Model (Dznotificationcenter)

The GitHub address is designed to be optimized by taking into account the above unpleasant areas:

Encapsulation of the operation to perform the function, only need to provide a message type of the solution block and message type corresponding to the protocol, when a message arrives, the message center will be unified unpack, and directly invoke the corresponding function of the observer.
The observer's maintenance mechanism is optimized (not yet done) to improve the efficiency of finding and deleting observers.
The usage of Dznotificationcenter is the same as that of nsnotificationcenter in registering and unregistering observers, and the difference is that you need to provide block for parsing messages when you use them. You can provide it in two ways.

The way to register directly

[Dzdefaultnotificationcenter Adddecodenotificationblock:^sel (nsdictionary *userinfo, NSMutableArray *__ Autoreleasing *params) {
nsstring* key = userinfo[@ "key"];
if (params!= NULL) {
*params = [Nsmutablearray new];
}
[*params Addobject:key];
Return @selector (handletestmessagewithkey:);
} Formessage:kdzmessagetest];

Implement the Dznotificationinitdelegaete protocol, which is recommended when using the viewer on a large scale throughout the project. This facilitates the unified management of all analytical methods.

-(Dzdecodenotificationblock) Decodenotification: (NSString *) message forcenter: (dznotificationcenter *) Center
{
if (message = = Kdzmessagetest) {return
^ (nsdictionary* userInfo, nsmutablearray* __autoreleasing* params) {
nsstring* key = userinfo[@ "key"];
if (params!= NULL) {
*params = [Nsmutablearray new];
}
[*params Addobject:key];
Return @selector (handleportmessage:);}
;
}
return nil;
}

In the process of using, in order to ensure that the same function can be recalled at the Observer, the Protocol for a certain message type can be implemented

@protocol dztestmessageinterface <NSObject>
-(void) Handletestmessagewithkey: (nsstring*) key;
@end

This ensures that the place where the observer is used does not have to repeat the number of spell names and parse the message content.

@interface Dzviewcontroller () <DZTestMessageInterface>
@end
@implementation Dzviewcontroller
....
-(void) Handletestmessagewithkey: (NSString *) key
{
Self.showLabel.text = [NSString stringwithformat:@ ' get Message with%@ ", key];
}
....

KVO

The full name of KVO is Key-value Observer, which is the key value observation. is a way of realizing the observer pattern without a central hub. A Subject object manages the Observer object that relies on it, and actively notifies the observer object when its state changes. Let's take a look at a complete example:

static nsstring* Const KKVOPATHKEY = @ "key";
@implementation dzkvotest
-(void) Setmessage: (dzmessage *) message
{
if (message!= _message) {
if (_ Message) {
[_message removeobserver:self forkeypath:kkvopathkey];
}
if (message) {
[message addobserver:self forkeypath:kkvopathkey options:nskeyvalueobservingoptionnew Context:nil ];
}
_message = message;
}
}
-(void) Observevalueforkeypath: (NSString *) KeyPath Ofobject: (ID) object change: (nsdictionary *) Change context: (void *) Context
{
if ([KeyPath isequal:kkvopathkey] && object = = _message) {
NSLog (@ ' Get%@ ', change); c18/>}
}
-(void) postMessage
{
_message.key = @ "ASDFASD";
}
@end

Complete a complete change notification process, following several procedures:

Registered Observer [message addobserver:self Forkeypath:kkvopathkey options:nskeyvalueobservingoptionnew Context:nil];
Change the value of the Subject object property, which triggers the notification that the change is sent _message.key = @ "ASDFASD";
In the established callback function, process the notification of changes received

-(void) Observevalueforkeypath: (NSString *) KeyPath Ofobject: (ID) object change: (nsdictionary *) Change context: (void *) Context
{
if ([KeyPath isequal:kkvopathkey] && object = = _message) {
NSLog (@ ' Get%@ ', change);
}
}

Cancellation of the Observer [_message Removeobserver:self Forkeypath:kkvopathkey];

KVO Realization Principle

In general, OBJC will automatically add the key-value observation feature to the property's properties, and you'll only need to write a @property (noatomic, assign) float age to get the Age's key-value observation function. And in order to further explore the principle of the implementation of KVO we first manually implement the KVO:

@implementation dzkvomanual
-(void) Setage: (int) Age
{
[self willchangevalueforkey:kkvopathage];
if (age!=_age) {
_age = age;
}
[Self didchangevalueforkey:kkvopathage];
}
The experience card will first call Automaticallynotifiesobserversforkey: Automaticallynotifiesobserversofage is invoked when the function is not there. This function should be a compiler that automatically adds a function that can be automatically prompted using Xcode. Very powerful indeed.
//+ (BOOL) automaticallynotifiesobserversofage
//{
//return NO;
}
+ (BOOL) Automaticallynotifiesobserversforkey: (NSString *) key
{
if (key ==kkvopathage) {
return NO;
}
return [Super Automaticallynotifiesobserversforkey:key];
}
@end

First, you need to manually implement the property setter method and call the Willchangevalueforkey: and Didchangevalueforkey methods before and after the setup operation, which is used to notify the system that the key's property value is about to be changed;

Secondly, to implement the class method Automaticallynotifiesobserversforkey, and to set the key to not automatically send a notification (return no). It should be noted here that key that is not manually implemented should be referred to super for processing.

In this manual implementation, the main is the manual implementation of the Subject object changes to the external broadcast process. How the follow-up broadcasts to the observer and the observer how the response we didn't achieve, in fact, these two processes Apple has been encapsulated very well, guess what, should be the subject object will maintain an observer queue, when its own attributes change, accept the notification, find the relevant attributes of the observer queue, Call Observevalueforkeypath in turn: (NSString *) KeyPath Ofobject: (ID) object change: (nsdictionary *) Change context: (void *) Context to broadcast changes. There is also a question, is the automatic implementation of KVO, the system and we do the manual implementation of the same thing?

Automatic realization of KVO and its principle

Let's take a closer look at what has changed for one instance of class Dzmessage during the use of KVO: Before using KVO:

When the setter method is invoked and the breakpoint is hit:

The Magical Discovery class's Isa pointer changes, our original class is called Dzmessage, and the class name becomes nskvonotifying_dzmessage after using the KVO. This shows what OBJC did to our class at runtime.

We found some clues from Apple's documentation Key-value observing implementation details.

The

Automatic Key-value observing is implemented using a technique called. The ISA pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, and among other data. When a observer is registered to the "an" Isa pointer of the observed object is modified, pointing To a intermediate class rather than at the true class. As a result the value of the ISA pointer does is not necessarily reflect the actual class of the instance. You are should never rely on the ISA pointer to determine class membership. Instead, you should use the class method to determine the class of object instance.

When an instance of a class uses KVO for the first time, the system dynamically creates a derived class of the class during run time, and the naming rule for the class is typically prefixed by nskvonotifying, with the original class name as the suffix. and point the object of the prototype's ISA pointer to the derived class. It also overloads the setter method that uses the Kvo property in the derived class to implement a true notification mechanism in the overloaded setter method, just as we manually implemented KVO in the previous. This is done by invoking the setter method based on the setting property, and by overriding the notification mechanism required by the KVO. Of course, if you want to change the property value by following the KVO property setting, you cannot implement KVO if you simply modify the corresponding member variable of the attribute.

The derived class also overrides the class method to "spoof" the External caller, which is the first class. Therefore, the object becomes the object of the derived class, so calls to the setter on the object invoke the overridden setter, activating the key-value notification mechanism. In addition, derived classes override the Dealloc method to free resources.

Pull model

A pull model is a subject object that notifies the observer, passing only a small amount of information or merely notifying the change. If the observer needs more specific information, the Observer takes the initiative to pull the data from the subject object. The pull model is more free than the push model, as long as the observer knows that something is going on, and when to get it, get it, or even get it can be decided independently. However, there are two problems:

If an observer responds too slowly, it may miss out on what was previously notified.
The observer must save a reference to the target object, and also need to understand the structure of the subject object, which makes the observer dependent on the subject object.
There may be more or less drawbacks to each design pattern, but they do solve the problem and there are more useful places to go. In the use of time, we need to weigh the pros and cons, make a suitable choice. The value of the engineer is embodied in the ability to find the most effective in the world of complex tools. And if the walnut has not been smashed open, not your hand strength is not a problem, but you choose the wrong tool, who let you have to use the door clamp, no hammer it!

Of course, the above paragraph belongs to the digression. In OBJC programming, the implementation of a typical pull model is delegate. Many people may disagree with my view that delegate should be a delegate model. Well, I do not deny that delegate is indeed an extremely typical way of implementing a delegate model. But that does not preclude him from being an observer model. In fact, the various design patterns are not entirely different. In the use and interpretation of the time, as long as you can make sense, and can solve the problem is good, there is no need to pester their names. And in the notice change this matter delegate indeed can solve the problem.

Let's look at an example of an observer who uses delegate to implement a pull model:

First implement a delegate to facilitate registration of observers, and callback functions

@class dzclient;
@protocol dzclientchangeddelegate <NSObject>
-(void) client: (dzclient*) client didchangedcontent: ( nsstring*) key;
@end
@interface dzclient:nsobject
@property (nonatomic, weak) id<dzclientchangeddelegate> delegate;
@property (nonatomic, strong) nsstring* key;
@end

Registered Observer

Dzappdelegate
dzclient* client = [dzclient new];
Client.delegate = self;
Client.key = @ "AA";

Notification of changes in the sending content when the Subject object's properties have changed

@implementation dzclient
-(void) Setkey: (NSString *) key
{
if (_key!= key) {
_key = key;
if ([_delegate respondstoselector: @selector (client:didchangedcontent:)]) {
[_delegate client:self didchangedcontent:@ "Key"]}


The observer receives changes to the subject's notice and takes the initiative to pull the changed content.

Dzappdelegate
-(void) Client: (Dzclient *) client didchangedcontent: (NSString *) key
{
if ([key IsEqual: @ "key"]) {
NSLog (@ "Get changed key%@", Client.key);
}


Generalized observer model

After introducing the observer model in the classical sense of the subject change passively, let's take a look at the generalized observer pattern. Of course, the classical observer model above is also a way of passing data. The generalized observer covers the classical observer pattern.

Often we have data that needs to be passed between "observer" and "Subject Object". In this case, the subject object may not be as industrious as the subject object in the Classic observer pattern, and will not stop broadcasting as the change occurs. In the generalized observer model, the subject object may be lazy, but the observer is informed of the change by constantly querying the state of the subject object.

We are familiar with the server CS architecture, always more typical with the Observer mode, the server is servo, waiting for the client access, the client through access to the server to get the latest content, rather than the server initiative push.

The reason for this is to present a concept of the generalized observer model. is to explore the nature of the observer model. It is convenient for us to understand the observer pattern more profoundly and to use it rationally. And we tend to focus more on notice changes, and the observer's fundamental purpose is to transfer the data between the observer and the subject object. The data may be changing the event itself, or it may be the content of the change, or maybe even some other content.

From the point of view of changing data transmission, we can realize this pattern and strategy are countless, such as the traditional network CS model, such as KVC and so on. This is not the first detail of the discussion here.

The above is described in this article for you to introduce the iOS observer design mode, I hope you like.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.