Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Analysis of single-case usage of iOS Development course

2025-01-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains the "singleton usage problem analysis of the iOS development tutorial". The explanation in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn the singleton usage problem analysis of the iOS development tutorial.

Introduction

Single case (Singletons) is one of the core modes of Cocoa. On iOS, singletons are very common, such as UIApplication,NSFileManager and so on. Although they are very convenient to use, they actually have many problems to pay attention to. So the next time you automatically complete a piece of dispatch_once code, think about the consequences of this.

What is a singleton?

The definition of a singleton is given in the book Design patterns:

Singleton pattern: ensures that there is only one instance of a class and provides a global access point to it.

The singleton pattern provides flexibility by providing an access point for the client class to generate a unique instance of the shared resource and access it.

In objective-c, you can create a singleton with the following code:

+ (instancetype) sharedInstance {static dispatch_once_t once; static id sharedInstance; dispatch_once (& once, ^ {sharedInstance = [[self alloc] init];}); return sharedInstance;}

It is convenient to use a singleton when a class can have only one instance and must be accessed from an access point, because using a singleton ensures that the access point is unique, consistent, and well-known.

Problems in a single case

Global statu

First of all, we should all agree that "globally variable state" is dangerous, because it makes the program difficult to understand and debug, and when it comes to reducing state code, object-oriented programming should learn from functional programming.

For example, the following code:

@ implementation Math {NSUInteger _ a; NSUInteger _ b;}-(NSUInteger) computeSum {return _ a + _ b;}

This code wants to calculate the sum of _ an and _ B and return. But in fact, there are a lot of problems with this code:

_ an and _ b are not taken as parameters in the computeSum method. Rather than looking for interface and knowing which variable controls the output of the method, finding implementation to understand is more covert, and concealment represents error prone. When preparing to modify the values of _ an and _ b to allow them to call the computeSum method, the programmer must be aware that changing their values will not affect the correctness of other code that contains the two values, which is particularly difficult in the case of multithreading.

Compare the following code:

+ (NSUInteger) computeSumOf: (NSUInteger) a plus: (NSUInteger) b {return a + b;}

In this code, the dependencies of an and b are very clear, you no longer need to change the state of the instance to call this method, and you don't have to worry about the side effects of calling this method.

So what does this example have to do with a single case? In fact, a single case is the overall state in sheep's clothing. A singleton can be used anywhere without clearly declaring subordination. Any module in the program can simply call [MySingleton sharedInstance] and get the access point of the singleton, which means that any side effects of interacting with the singleton may affect a random piece of code in the program, such as:

Interface MySingleton: NSObject+ (instancetype) sharedInstance;- (NSUInteger) badMutableState;- (void) setBadMutableState: (NSUInteger) badMutableState;@end@implementation ConsumerA- (void) someMethod {if ([[MySingleton sharedInstance] badMutableState]) {/ / do something... } @ end@implementation ConsumerB- (void) someOtherMethod {[[MySingleton sharedInstance] setBadMutableState:0];}

In the above code, ConsumerA and ComsumerB are two completely separate modules in the program, but the methods in ComsumerB affect the behavior in ComsumerA because this state change is passed through a singleton.

In this code, it is the global and stateful nature of the singleton that leads to the implicit coupling between ComsumerA and ComsumerB, two seemingly unrelated modules.

Object life cycle

Another major problem with singletons is their life cycle.

For example, suppose an app needs to implement a function that allows users to see their friend list, each friend has his or her own avatar, and we also want the app to download and cache these friends' avatars. At this time, by learning the knowledge of singletons, we are likely to write the following code:

Interface MyAppCache: NSObject+ (instancetype) sharedCMyAppCache;- (void) cacheProfileImage: (NSData *) imageData forUserId: (NSString *) userID;- (NSData *) cachedProfileImageForUserId: (NSString *) userId;@end

This code looks perfectly fine and works well, so app continued to develop until one day we decided to add the "logout" feature to app. Suddenly we found that user data was stored in a global singleton. When the user logs out, we want to erase the data and create a new MyAppCache for the new user when he logs in.

But the problem lies with the singleton, because the definition of the singleton is: "create once, live forever" instance. In fact, there are many ways to solve the above problem, and we may be able to destroy this singleton when the user logs out:

Static MyAppCache * myAppCache;+ (instancetype) sharedMyAppCache {if (! myAppCache) {myAppCache = [[self alloc] init];} return myAppCache;} + (void) tearDown {myAppCache = nil;}

The above code distorts the singleton pattern, but it works.

In fact, this method can be used to solve this problem, but the cost is too high. The most important thing is that we abandoned dispatch_once, which ensures thread safety in method calls. Now all code calling [MyAppCache shareMyAppCache] gets the same variable, so you need to be clear about the order in which the MyAppCache code is executed. Imagine that this method is called in the background to save the image when the user is logging out.

On the other hand, implementing this method requires ensuring that the tearDown method is not called before the background task is completed, or that the background task is cancelled when the tearDown method is executed. Otherwise, another new MyAppCache will be created and the stale data will be saved.

However, because the singleton does not have a clear owner (because the singleton manages its own life cycle), destroying a singleton is very difficult.

So you might think, "then don't make MyAppCache a singleton." In fact, the problem is that the life cycle of an object may not be well determined at the beginning of the project, if it is assumed that the life cycle of an object will match the life cycle of the entire program, which will greatly limit the extensibility of the code. this will be painful when product requirements change.

So all of the above is to illustrate a point: "singletons should only maintain the global state, and the life cycle of that state is consistent with the life cycle of the program." For singletons that already exist in the program, critical review is required.

It is not good for testing

This part was mentioned in the previous chapter, but I think testing is a very important part of software development, so open a separate chapter on this section and add some personal opinions.

Because singletons survive throughout the life cycle of app, even when tests are executed, one test may affect another, which is a big taboo in unit testing. Therefore, it is necessary to effectively destroy a singleton and maintain the singleton thread safety feature when conducting unit testing. But as I mentioned above:

"but because the singleton does not have a clear owner (because the singleton manages its own life cycle), destroying a singleton is very difficult."

It seems that the two are self-contradictory, but in fact, you can choose to simplify the singleton. Instead of having a variety of singletons, it is better to have only a "real" singleton ServiceRegistry and other "potential" singletons to be referenced by ServiceRegistry, so that other singletons have an owner that can be destroyed in time during unit testing, ensuring the independence of the unit test.

On the other hand, the existence of ServiceRegistry makes other "singletons" no longer singletons, which makes it easier for singletons that were previously difficult to mock in TDD.

Thank you for your reading, the above is the "iOS development tutorial singleton use problem analysis" content, after the study of this article, I believe you have a deeper understanding of the iOS development tutorial singleton use problem analysis, the specific use also needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report