Do you really understand the iOS proxy design mode?

Source: Internet
Author: User
Tags notification center

Do you really understand the iOS proxy design mode?

In projects, we often use the proxy design mode. This is a message transmission method in iOS, and some parameters can be passed through this method. This article covers the skills and principles of proxy use and the memory management of proxy. Through these knowledge, I will truly appreciate the secrets of agents. There are a lot of articles written, but they are all dry products. I can write them down. I wonder if you have the patience to read them. My skills are limited. If you have any questions or are not mentioned in this article, please help me to point out. Thank you very much!

Message transmission mode in iOS

There are many message transmission methods in iOS. Here we will briefly introduce various message transmission methods.

Notification: in iOS, the notification center receives messages and broadcasts messages. It is a one-to-many message transmission method.

Proxy: it is a general design mode. It supports proxy well in iOS and consists of three parts: proxy object, delegate, and protocol.

Block: a callback method introduced in iOS4.0. You can write the callback processing code directly in the block code block, which looks like the logic is clear and the code is neat.

Target action: This method is called by passing an object to another class and using this object as a target in another class. It is similar to the proxy in terms of memory.

KVO: NSObject's Category-NSKeyValueObserving. It monitors the changes of a value through attribute listening. When the value changes, KVO's callback method is called.

Of course, there are other callback methods. Here is just a simple list.

Basic use of proxy

Proxy is a general design mode. In iOS, proxy design mode is well supported and has a specific syntax to implement proxy mode. The OC language can implement the Protocol through @ Protocol.

The agent consists of three parts:

  • Protocol: used to specify what the proxy can do and what the proxy must do.

    Proxy: implements the functions required by the entrusting party based on the specified protocol.

    Delegate: Specify the proxy's functions based on the specified protocol.

    Here we use a diagram to illustrate the relationship between the three parties:

    Legend

    Protocol-Protocol Concept

    We can see the relationship between the three parties. In actual application, the agreement is used to specify the actions of both parties. The content of the agreement is generally a list of methods. Of course, attributes can also be defined, I will introduce the attributes defined in the Protocol in subsequent articles.

    The Protocol is a public definition. If it is only used by a class, we often write it in a class. If multiple classes use the same Protocol, we recommend that you create a Protocol file to define the Protocol in this file. The following protocols can be inherited. For example, we often use UITableView because it inherits from UIScrollView. Therefore, we also inherit UIScrollViewDelegate. We can use the proxy method to obtain status parameters such as UITableView offset.

    The Protocol can only define a set of public interfaces, similar to the role of a constrained proxy. But it cannot provide specific implementation methods. The implementation methods must be implemented by proxy objects. Protocols can inherit from other protocols and inherit from multiple protocols. In iOS, objects do not support multi-inheritance, while protocols can inherit from multiple protocols.

    1 2 3 4 // The current Protocol inherits three protocols, so that the list of methods in the other three protocols will be inherited. @protocolLoginProtocol -(void)userLoginWithUsername:(NSString*)usernamepassword:(NSString*)password; @end

    The Protocol has two modifiers: @ optional and @ required. If a protocol is not declared, it is in the @ required State by default. The two modifiers only specify whether the proxy is forced to comply with the Protocol. If the @ required status method proxy does not comply with the modifier, a yellow warning is reported, which only serves as a constraint and has no other functions.

    Whether it is @ optional or @ required, you need to make a judgment when the delegate calls the proxy method to determine whether the agent implements the current method, otherwise it will cause a crash.

    Example:

    1 2 3 4 // Determine whether the proxy object implements this method. Failure to implement this method will cause a crash. if([self.delegaterespondsToSelector:@selector(userLoginWithUsername:password:)]){ [self.delegateuserLoginWithUsername:self.username.textpassword:self.password.text]; }

    Below we will use a small example to explain this problem:

    Example: If I'm typing code at the company, I'm so happy. Suddenly I'm thirsty and want to drink a bottle of black tea. Then I can pick up my cell phone and set a black tea on the takeout app. Then the takeout app will place an order for the store and ask the store to send it to me.

    In this process, the take-out app is my agent, and I am the entrusting party. I bought a bottle of black tea and paid the take-out app money. This is the purchase agreement. I only need to buy from the take-out app, and the specific operations are handled by the take-out app. I only need to finally receive this bottle of black tea. The money I pay is the parameter, and the final black tea is the processing result.

    However, when I buy black tea, I also want to have a pizza. I need to order food from the Pizza Hut app. The takeout app above does not have this function. I bought a pizza from Pizza Hut. Pizza Hut made this pizza for me as my proxy and finally delivered it to me. This is the proxy object, and I am the entrusting party.

    Proxy

    In iOS, a proxy can have multiple entrusting parties, and one entrusting party can also have multiple proxies. I have designated two agents, take-out app and Pizza Hut. You can also specify multiple agents such as McDonald's, and the entrusting party can also serve multiple agents.

    Proxy objects can be reused in many cases. You can create multiple proxy objects for multiple delegate services. The following is a small example to illustrate the reuse of controller proxies.

    The following is a simple Proxy:

    First, define a protocol class to define public protocols.

    1 2 3 4 5 #import @protocolLoginProtocol @optional -(void)userLoginWithUsername:(NSString*)usernamepassword:(NSString*)password; @end

    Define the delegate class. Here, a user login function is implemented to pass the account and password after the user logs on, and a proxy is provided to process specific login details.

     

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #import#import"LoginProtocol.h" /** * The current class is the delegate class. After a user logs on, the proxy object is allowed to implement specific login details. The delegate class does not need to know the specific implementation details. */ @interfaceLoginViewController:UIViewController // Set proxy objects through Properties @property(nonatomic,weak)iddelegate; @end Implementation: @implementationLoginViewController -(void)loginButtonClick:(UIButton*)button{ // Determine whether the proxy object implements this method. Failure to implement this method will cause a crash. if([self.delegaterespondsToSelector:@selector(userLoginWithUsername:password:)]){ // Call the logon method of the proxy object to implement the logon method. [self.delegateuserLoginWithUsername:self.username.textpassword:self.password.text]; } }

    The delegate does not need to know the implementation details to implement the specific login process.

     

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // Comply with the logon Protocol @interfaceViewController() @end @implementationViewController -(void)viewDidLoad{ [superviewDidLoad]; LoginViewController*loginVC=[[LoginViewControlleralloc]init]; loginVC.delegate=self; [self.navigationControllerpushViewController:loginVCanimated:YES]; } /** * The agent implements specific login details */ -(void)userLoginWithUsername:(NSString*)usernamepassword:(NSString*)password{ NSLog(@"username:%@,password:%@",username,password); }

    How proxy works

    Proxy implementation process

    In iOS, proxy is essentially the transfer and operation of the proxy object memory. After the delegate class sets the proxy object, we actually only use an id pointer to make a weak reference to the proxy object. The delegate allows the proxy to perform operations. In fact, the delegate class sends a message to the object pointed to by the id pointer. The object pointed to by this id pointer is the proxy object.

    Proxy Principle

    Through the above figure, we found that the delegate's proxy attribute is essentially the proxy object itself, and the delegate proxy is set to the proxy property pointer pointing to the proxy object, it is equivalent that the proxy object only calls its own method in the delegate. If the method is not implemented, it will cause a crash. From the crash information, we can see that the crash is caused by the failure of the agent to implement the method in the Protocol.

    The Protocol is just a syntax. It declares that the proxy attribute in the delegate can call the method declared in the protocol, but the implementation of the method in the Protocol is still completed by the proxy, the Protocol and the entrusting party do not know whether the agent has completed or how to complete it.

    Agent Memory Management

    Why do we use weak when setting proxy attributes?

    The pointer we define is of the _ strong type by default, and the attribute is essentially a member variable and composed of the set and get methods. The strong type pointer will cause a strong reference, it will definitely affect the lifecycle of an object, which forms a circular reference.

    Strong reference

    Because the proxy object uses a strong reference pointer, it references the created principal LoginVC object and becomes the agent of LoginVC. This will cause the delegate attribute of LoginVC to strongly reference the proxy object, leading to the issue of circular reference. Eventually, both objects cannot be released normally.

    Weak reference

    We set the delegate attribute of the LoginVC object to the weak reference attribute. In this way, when the life cycle of the proxy object exists, it can work normally for us. If the proxy object is released, the delegate and proxy object will not be Crash caused by memory release.

    However, there is still a problem. Will it really not crash?

    The following two methods are weak reference proxy objects, but the first method will not cause a crash after the proxy object is released, and the second method will cause a crash.

     

    1 2 @property(nonatomic,weak)iddelegate; @property(nonatomic,assign)iddelegate;

    Weak and assign are non-having pointers. the pointer variables modified by these modifiers do not change the reference count of the referenced object. However, after an object is released, weak automatically points the pointer to nil, but assign does not. In iOS, messages sent to nil do not crash, so assign will cause the unrecognized selector sent to instance error.

    Therefore, if we modify the proxy attributes, we should use weak to modify them, which is safer.

    Controller slimming-proxy object

    Why use proxy objects?

    As projects become more and more complex, controllers become more and more bloated as business increases. In this case, many people think of the recently popular MVVM design mode. However, the learning curve of this model is very difficult to grasp and can be used for new projects. For a large and medium-sized project that is already very complex, it is not very easy to change the framework layer.

    UITableView should be available for many controls used in the project. Some pages often have a lot of processing logic for UITableView, which is a big reason for the bloated controller. For this problem, we can consider slimming the controller and slimming the controller through proxy objects.

    What is a proxy object?

    This is the general controller using UITableView (the picture is ugly, mainly meaning understanding)

    Common Writing Method

    This is the structure of the optimized controller.

    Proxy object

    We can see from the above two figures that the delegate and DataSource of UITableView are taken out separately, controlled by a proxy object class, and only the logic that must be processed by the Controller is passed to the Controller for processing.

    The data processing, presentation logic, and simple logic interaction of UITableView are all processed by the proxy object, and the logic processing related to the Controller is transferred to the Controller for processing. As a result, the work of the controller is much less, the coupling degree is also greatly reduced. In this way, we only need to submit the work to be processed to the proxy object and input some parameters.

    Below we use a piece of code to implement a simple proxy object

    Proxy object. h file Declaration

     

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedefvoid(^selectCell)(NSIndexPath*indexPath); /** * Proxy object (the UITableView protocol must be declared in the. h file, otherwise the outside world will report a yellow warning when using it, which looks uncomfortable) */ @ InterfaceTableViewDelegateObj: NSObject [UITableViewDelegate, UITableViewDataSource] (for recognition problems, change angle brackets to square brackets) /** * Create a proxy object instance and upload the data list * When a proxy object transmits messages, messages are delivered through blocks. * @ Return returns the Instance Object */ +(instancetype)createTableViewDelegateWithDataList:(NSArray*)dataList selectBlock:(selectCell)selectBlock; @end

     

    Proxy object. m file implementation

     

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 28 29 30 31 32 33 34 35 36 38 39 40 41 42 43 #import"TableViewDelegateObj.h" @interfaceTableViewDelegateObj() @property(nonatomic,strong)NSArray*dataList; @property(nonatomic,copy)selectCellselectBlock; @end @implementationTableViewDelegateObj +(instancetype)createTableViewDelegateWithDataList:(NSArray*)dataList selectBlock:(selectCell)selectBlock{ return[[[selfclass]alloc]initTableViewDelegateWithDataList:dataList selectBlock:selectBlock]; } -(instancetype)initTableViewDelegateWithDataList:(NSArray*)dataListselectBlock:(selectCell)selectBlock{ self=[superinit]; if(self){ self.dataList=dataList; self.selectBlock=selectBlock; } returnself; } -(UITableViewCell*)tableView:(UITableView*)tableViewcellForRowAtIndexPath:(NSIndexPath*)indexPath{ staticNSString*identifier=@"cell"; UITableViewCell*cell=[tableViewdequeueReusableCellWithIdentifier:identifier]; if(!cell){ cell=[[UITableViewCellalloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:identifier]; } cell.textLabel.text=self.dataList[indexPath.row]; returncell; } -(NSInteger)tableView:(UITableView*)tableViewnumberOfRowsInSection:(NSInteger)section{ returnself.dataList.count; } -(void)tableView:(UITableView*)tableViewdidSelectRowAtIndexPath:(NSIndexPath*)indexPath{ [tableViewdeselectRowAtIndexPath:indexPathanimated:NO]; // Pass the click event through block self.selectBlock(indexPath); } @end

    The calling of the external controller is very simple, just a few lines of code.

    1 2 3 4 5 6 self.tableDelegate=[TableViewDelegateObjcreateTableViewDelegateWithDataList:self.dataList selectBlock:^(NSIndexPath*indexPath){ NSLog(@"% Ld row cell clicked",(long)indexPath.row); }]; self.tableView.delegate=self.tableDelegate; self.tableView.dataSource=self.tableDelegate;

    In the controller, you only need to create a proxy object class and hand over the delegate and dataSource of UITableView to the proxy object for processing, so that the proxy object becomes the proxy of UITableView, it solves the problem of bloated controllers and the decoupling from UITableView.

    The above Code simply implements the click cell function. If you have other requirements, most of them can be processed in the proxy object. Another benefit of using the proxy object class is that if multiple UITableView logic is the same or similar, the proxy object can be reused.

    Informal agreement

    Introduction

    Before the official @ Protocol has been introduced in iOS2.0, the function of implementing the Protocol is mainly to add Category to NSObject. Compared with @ Protocol introduced after iOS2.0, this Category method is called informal Protocol.

    As mentioned above, informal protocols generally exist in the form of NSObject Category. Because it is a Category of NSObject, all NSObject-based subclasses accept the defined informal protocol. For @ Protocol, the compiler checks for syntax errors during compilation, while informal protocols do not check for implementation.

    In informal protocols, there is no @ optional or @ required for @ Protocol. When Calling @ Protocol, you need to determine whether the method is implemented.

    1 2 3 4 // Because the Category is used, you need to use self to determine whether the method is implemented. if([selfrespondsToSelector:@selector(userLoginWithUsername:password:)]){ [selfuserLoginWithUsername:self.username.textpassword:self.password.text]; }

    Informal Protocol example

    A large number of informal protocols were also used in early iOS versions. For example, CALayerDelegate is an implementation of informal protocols. Informal protocols are essentially Category.

    1 2 3 4 5 6 @interfaceNSObject(CALayerDelegate) -(void)displayLayer:(CALayer*)layer; -(void)drawLayer:(CALayer*)layerinContext:(CGContextRef)ctx; -(void)layoutSublayersOfLayer:(CALayer*)layer; -(nullableid)actionForLayer:(CALayer*)layerforKey:(NSString*)event; @end

    Proxy and block selection

    There are many callback methods in iOS, and the proxy and block functions are more similar. They both perform callback directly. Which one should we use or which one is better?

    In fact, there is no better way to transmit these two messages. Which of the following is difficult to say? We should distinguish what should be used under what circumstances and what should be used more appropriate! Next, I will briefly introduce the choice of proxy and block in different situations:

    Use delegate to transmit multiple messages. When multiple messages are transmitted, it looks clearer to use delegate. The block is not very good. In this case, the block is not easy to maintain and looks very bloated and awkward. For example, if many proxies in UITableView of UIKit are implemented by block, let's think about this scenario. Here we don't need to write code as an example... It seems intolerable.

    The proxy attribute of a delegate object can only have one proxy object. If you want the delegate object to call the callback of multiple proxy objects, block should be used.

    Proxy

    In the figure above, proxy 1 can be set, and proxy 2 and proxy 3 are set with a cross, because this step is incorrect. As we have mentioned above, delegate is only an address for saving a proxy object. If multiple proxies are set, they are equivalent to re-assigned values. Only the last proxy is assigned a value.

    It is best not to use delegate for a singleton object. The singleton object is always the same object. If delegate is used, the delegate attribute we mentioned above will be re-assigned, in the end, only one object can respond to the proxy method normally.

    In this case, we can use the block method. It is okay to use the block in multiple objects in the main thread. Next we will use a loop of brute force to test whether the block is correct.

    1 2 3 4 5 6 7 8 9 NSOperationQueue*queue=[[NSOperationQueuealloc]init]; queue.maxConcurrentOperationCount=10; for(inti=0;i<100;i++){ [queueaddOperationWithBlock:^{ [[LoginViewControllershareInstance]userLoginWithSuccess:^(NSString*username){ NSLog(@"TestTableViewController:%d",i); }]; }]; }

    In the preceding example, a new queue is created with NSOperationQueue, and the maximum number of concurrent threads is set to 10. Then, a 100 cycle is created. In the case of multithreading, we can test whether a singleton can be used normally in the case of block. The answer is yes. However, we still need to note that in the case of multithreading, because it is a singleton object, we apply a lock to the necessary parts of the block to prevent resource theft.

    The proxy is optional, while the block can only pass an nil In a parameter when calling a method, but this is not a big problem and can be ignored without code cleansing.

    1 2 3 4 5 6 [selfdownloadTaskWithResumeData:resumeData sessionManager:manager savePath:savePath progressBlock:nil successBlock:successBlock failureBlock:failureBlock];

    The proxy is more specific, and the block is more result-oriented. From the perspective of the design pattern, proxy is more process-oriented, while block is more result-oriented. For example, we use NSXMLParserDelegate proxy for XML parsing. NSXMLParserDelegate has many proxy methods. NSXMLParser will call these methods continuously to pass some conversion parameters. This is the NSXMLParser parsing process, it is more appropriate to present these data through proxy. For example, if a network request is returned, it is better to display it through success and failure code blocks.

    In terms of performance, the performance consumption of a block is slightly higher than that of a delegate, because the block involves operations such as copying from the stack to the heap, and the consumption of time and space is greater than that of a proxy. The proxy only defines a method list. Add a node to the objc_protocol_list object that complies with the Protocol, and send a message to the object that complies with the protocol at runtime. This article is not about block, so I will not describe it too much. Tang Qiao introduced block in an article, which is highly recommended for further study.

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.