IOS martial arts esoteric article summary

Writing in the front

Recently, I reread the source code of AFNetworking 4.x. Also combed some excellent code details and interview test points, listed down, found that this library is small and delicate, is the treasure of beginners.

AFN address in GitHub Secret Demo that may be used in this section

What about open source libraries?

First said a digression, reading high quality open source code library, is absolutely programmers quickly improve themselves effective way, and how efficient to read the source code is also a problem, I do not know whether there is and I before, encountered reading is read, but the total feeling harvest is not big.

Here are some of my code reading experiences:

  1. Think more, ask more questions, like

    • What is the overall code structure? What are the relationships between classes? Why do they do that?
    • Does the code involve multithreading, and what is the threading model? What kind of problem applies to this multithreaded solution?
    • What design patterns are used in the code? How does it work?
  2. You can also pay attention to the details of the code, don’t let go of unfamiliar usage, more research to strengthen the foundation

    Some of the best code details about AFNetworking have also been compiled here for review

  3. Be sure to take notes and summarize. Sharing is even better

    Referring to the Feynman method of learning, I think this is the best way to deepen understanding and strengthen memory. As memory declines with age, having a note to review can save you a lot of time memorizing it again. In addition, share more with people, and exchange verification, but also for their own shortcomings.

AFNetworking 4.x

Back to AFNetworking, the code structure of AF should be familiar to most people. Here I will briefly introduce it. The code structure of AFNetworking 4.x is much simpler than 2.x, mainly due to Apple’s optimization of the network related API. The overall code has the following parts:

NSURLConnection was removed from the AF3.x version

  • AFURLSessionManager/AFHTTPSessionManager

    Here is the core of AF code, mainly responsible for network request initiation, callback processing, is a layer of encapsulation on the system network related API. Most of the logic is handled in AFURLSessionManager, which provides convenience methods for HTTP requests. If you need to extend the functionality of other protocols, such as FTP, consider creating a subclass from AFURLSessionManager.

  • AFURLRequestSerialization/AFURLResponseSerialization

    The two brothers do some of the serialization work. AFURLRequestSerialization is introduced to the parameters of the structure into NSURLRequest, such as custom header, some post or get parameters and so on. AFURLResponseSerialization mainly will return to NSURLResponse processing system as we need responseObject, such as json, XML, image and so on.

  • AFSecurityPolicy

    Handles HTTPS related public key and authentication logic. With the APPLE ATS enabled, basic HTTPS has become standard. Although it is common to use the CA directly to verify the server’s public key, no additional configuration is required. But from here on, by the way, HTTPS related knowledge is also common, specific interview questions can be read below

  • AFNetworkReachabilityManager

    This is a separate module that provides the function of obtaining the current network state.

  • UIKit+AFNetworking

    So this is basically just to provide a little bit of a convenience method of UIkit by Category

AFURLSessionManager/AFHTTPSessionManager

① Manager initialization

Speaking of manager initialization. I would like to ask you what is the design mode of Manager? I’m guessing a lot of people would say singleton, haha, if you say that in an interview, the interviewer will probably tell you to go back and wait. In fact, the design pattern used by manager is the factory design pattern.

Look at the parent classAFURLSessionManagertheinitWithSessionConfigurationmethodsAmong them, there is another test point oh. namelyself.operationQueue.maxConcurrentOperationCount = 1;Why is that? Can we open a few more threads? —The answer is noIn view of the security concerns of some multithreaded data access. Meanwhile, Apple officially told us no

② Request method encapsulation

We all know that a complete request request should include the request line + the request header + the request body. Request lines will be covered later, but first, how AF encapsulates the header and body of the request.

In order togetLet’s see, request for example, enterAFIn thegetMethod of request implementationThis is basically returning onedataTaskandStart network request, does not tell us how to encapsulate the request header, we continue to enterdataTaskWithHTTPMethod:methodsThe main functions of this method are:

  • 1.To generate the request
  • (2) byrequestgeneratedataTask.

So how does it encapsulate the generated request? Keep up with my motorcade and continue inrequestWithMethod:methodsrequestWithMethodIn the method, three things are done:

  • (1) to createmutableRequestAnd set its request method;
  • ② Set some properties of the current class tomutableRequestConfiguration of the request header;
If we do not set the timeout, we use the default timeout of 60 seconds...Copy the code

This is mainly usedKVOResponsive programming techniques

  • ③ Encode the parameters to be passed and set tomutableRequestRequest parameter encapsulation, the callrequestBySerializingRequest:methods

This method mainly does the following things:

  • ① Fetch from the current request queueself.HTTPRequestHeadersTo the parameter to be requestedrequestIn the.
  • (2) theNetwork request parametersconvertNSStringType, that’s the right stepParameters are transcoded— This is our focus, mainly callAFQueryStringFromParametersMethods.
  • ③ Concatenate the request method tourlIn:GET, HEAD, DELETEThis a fewmethodthequeryIs spliced intourlIn the back, andPOST, PUT,Is thequeryJoining together tohttp bodyIn the.

Now let’s focus on,AFHow to convert the parameter dictionary we passed into a string. Enter theAFQueryStringFromParametersmethodsCall recursively and parse until nothing is parsedArray, DIC, setThen return the final parameter, and proceed with step ③.

③ The relationship between task and agent

And then the above analysis, which we just didAFHow to makeNetwork request parametersconvertNSStringType, now let’s seeAFHow do you concatenate request methods tourlThis brings us to our main pointtaskwithThe agentEnter intodataTaskWithRequest:methods One thing to notice here is that this method is made up ofAFURLSessionManagerManaged, not managedAFURLSessionManagerTaskDelegate

So why are they connected and connected in the end? Want to know? Let’s move on if you wantIn combination with the previous we have the holding relationship at this timemanager-->session-->task--->delegate--->managerDoesn’t that make a circular reference? How does AF solve this problem? And then back to ours

After the above process, the call is next[dataTask resume];Method. ThisresumeThere is a problem, iron,AFThere are some dirty operations on it at the bottom.. Here I will reveal his secret. He actually made an inner class here_AFURLSessionTaskSwizzling, in this class willresumeandaf_resumeIt’s an exchange. Let’s take a lookAFMake SAO operationaf_resumeThe implementation of theIn fact, in this caseAFInside the frame, we can get it alltaskStatus. who is this notification for? Let’s have a lookTo theAFURLSessionManager.because it is a big butler, a lot of authority. Ha ha.

Finally, to summarize the whole process of this part, as shown below

AFURLRequestSerialization/AFURLResponseSerialization

① NSObject, NSSecureCoding, NSCopying protocol

Came toAFURLRequestSerializationClass declaration address, we’ll see that it followsNSObject, NSSecureCoding, NSCopyingThese three protocols.Mainly is to letAFURLRequestSerializationhavecopyandThe archiveAnd a series of functions.

② Data encapsulation of multiple forms

What about multiple forms? To look directly at the source codeEnter via the above method (called when uploading an image or a breakpoint continues)multipartFormRequestWithMethod:methodsLet’s start with productionformDatatheinitWithURLRequest:methodsLet’s seeAFCreateMultipartFormBoundaryThis delimiterAnd then let’s go back and continueAFIs how to deal withparametersIt will perform a series of formatting to generate the correspondingAFQueryStringPairYeah, and then finally the concatenation callappendPartWithFormDatamethods

The whole process is going to be

Notice hereMore than the formtheContent-TypeAnd the generalpostForms are different and commonContent-TypeisMore than the formIt is Let’s seecontentLengthHow is it put together

(2) the Stream flow

So how is the data processed after stitching? So let’s go backThat’s set in this methodstream.Let’s take a look firstHTTPBodyStreamattributeSource code tells usstreamIt’s off by default, so we’re going to turn it on before we read it. And then after callingNSInputStreamRelevant agents.HTTPBodyStreamandHTTPBodyIt is incompatible. Only one can be set.

In the callresumeMethod is preceded by the following method.This method is mostly calledbodypartInterface method:read:... maxLengthThen enter thetransitionToNextPhaseIn this method we open ourstreamthentransitionToNextPhaseWhen is this method called? Actually it is ininitIt’s called when it’s initialized

(3) AFURLResponse

So when is it comingAFURLResponse?Manager -- task -- Request completionWhen the background has a return time. Go back toNSURLSessionTaskDelegateIn the proxy methodif(error)elsePart of theThis includes various serializations that we are developingLet’s see what we use[AFJSONResponseSerializer responseObjectForResponse:data:error:]methods

  • First look at the verification request judgment piece, he is to verify what?
  • ② If the verification is passed, it will be carried outJSONData serialization, there’s one thing that needs to be said about this

Removes data that is returned as null.

The flow of this section is roughly as follows

AFSecurityPolicy

(1) introduction of HTTP

(2) the HTTPS

The difference between HTTPS and HTTP

HTTPS = HTTP + SSL/TLS SSL is a Secure protocol that provides security and data integrity for network communication. TLS stands for Transport Layer Security. That HTTPS is secure HTTP.

HTTPS connection establishment process

HTTPS uses both symmetric and asymmetric encryption to ensure security and efficiency. Three keys are involved in the transmission process:

  • The public and private keys on the server are used for asymmetric encryption

  • Random key generated by the client for symmetric encryption

As shown in the figure above, the HTTPS connection process can be roughly divided into eight steps:1. The client accesses the HTTPS connection

The client sends the security protocol version number, list of encryption algorithms supported by the client, and a random number C to the server.

2. The server sends the certificate to the client

After receiving the key algorithm accessory, the server compares it with the list of supported encryption algorithms. If the list does not match, the server disconnects from the server. Otherwise, the server selects a symmetric algorithm (such as AES), a public key algorithm (such as RSA with a specific key length), and a MAC algorithm from the algorithm list to send to the client. The server has a key pair, that is, a public key and a private key, which are used for asymmetric encryption. The server stores the private key and cannot disclose it. The public key can be sent to anyone. The digital certificate and random number S are sent to the client when the encryption algorithm is sent

3. The client authenticates the server certificate

The server public key is checked to verify its validity. If the public key is faulty, HTTPS transfer cannot continue.

4. The client assembles the session key

If the public key is qualified, the client will use the server public key to generate a pre-master Secret (PMS), and assemble the session Secret from the pre-master key and random numbers C and S

5. The client encrypts the former master key and sends it to the server

The former master key is asymmetrically encrypted through the public key of the server and sent to the server

6. The server decrypts the former master key through the private key

After receiving the encrypted message, the server decrypts it with the private key to obtain the master key.

7. The server assembles the session key

The server assembles the session key from the pre-master key and random numbers C and S. At this point, both the server and client know the master key for the session.

8. Data transmission

The client receives the ciphertext sent by the server and decrypts it symmetrically using the client key to obtain the data sent by the server. Similarly, the server receives the ciphertext sent by the client and decrypts it symmetrically using the server key to obtain the data sent by the client.

Conclusion:

Session key = random S + random C + former primary key

  • The ESTABLISHMENT of HTTPS connections uses asymmetric encryption, which is a time-consuming encryption method

  • Symmetric encryption is used in subsequent communication to reduce performance loss caused by time consumption

  • Symmetric encryption encrypts actual data, while asymmetric encryption encrypts client keys required by symmetric encryption.

Symmetric and asymmetric encryption

1, symmetric encryption

Use the same set of keys to encrypt and decrypt. Symmetric encryption usually includes DES,IDEA, and 3DES encryption algorithms.

2, asymmetric encryption

Encryption and decryption algorithms using public and private keys. A Public Key and a Private Key are a Key pair (namely, a Public Key and a Private Key) obtained through an algorithm. The Public Key is the Public part of the Key pair, and the Private Key is the Private part. The Private Key is usually stored locally.

  • Encrypting with public key requires decrypting with private key. Whereas encryption with a private key requires decryption with a public key (digital signature).

  • Asymmetric encryption is secure compared to symmetric encryption because the private key is stored locally.

However, asymmetric encryption takes up to 100 times more time than symmetric encryption, so it is usually used in combination with symmetric encryption.

Common asymmetric encryption algorithms include RSA, ECC (for mobile devices), Diffie-Hellman, El Gamal, AND DSA (for digital signature).

In order to ensure that the client can confirm that the public key is the public key of the website they want to visit, the concept of digital certificate is introduced. Since there is a certificate issuing process at the first level, a certificate chain appears, and the root CA is at the top of the certificate chain.

(3) AFSecurityPolicy

Take a look atAFThe source of

So how does it get the public key? Continue to follow upsetPinnedCertificatesmethodsThere are two possible authentication modes during public key acquisition: unidirectional authentication (server) and bidirectional authentication (customer and server): All certificate information is verified during the public key transfer process. This will be entered later in the proxy methodevaluateServerTrust:forDomain:Method to verify that the server is trustworthy.If your certificate is untrusted, he will enterThat is, if your certificate isCADo not need to be processed, is a self-signed need to set as the root certificate.

AFNetworkReachabilityManager

Let’s start with a flow chartWithout further discussion of the source code, this class mainly uses the global singleton design patternThen callmanagermethodsTo follow upmanagerForAddressmethods

returnReachabilityManagerObject after callstartMonitoringmethods And then according to the networkflagConvert to the network state calls we use in developmentAFNetworkReachabilityStatus()

UIKit+AFNetworking

AFAutoPurgingImageCache

This class is used to manage cached images in memory. It provides the maximum memory capacity and the preferred memory capacity. When the maximum memory capacity is reached, the oldest unused cached images are purged until they fall below the preferred memory capacity. Let’s see how this function is implemented.

AFImageCache agreement

This protocol defines methods for adding, deleting, modifying, and viewing cached images. These methods are synchronized and secure:

@protocol AFImageCache <NSObject>
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier;
- (BOOL)removeImageWithIdentifier:(NSString *)identifier;
- (BOOL)removeAllImages;
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier;
@end
Copy the code

These methods involve the input parameter identifier, which can be used as the image ID to find the image, generally represented by the image name. Of course, the network image can also be represented by THE URL. For this point, AF extended this protocol.

AFImageRequestCache
@protocol AFImageRequestCache <AFImageCache>
// The default implementation is YES
- (BOOL)shouldCacheImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
/ / add and check methods, support the incoming a request object, and to request the URL. AbsoluteString identifier for the picture, at the same time can also be splicing AdditionalIdentifier after the default identifier
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
@end
Copy the code

After looking at the definition of the protocol, let’s move on to the AFAutoPurgingImageCache class, the main implementation of the protocol

AFAutoPurgingImageCache

In addition to implementing AFImageCache protocol related methods, this class also adds memory control properties:

// Maximum memory
@property (nonatomic.assign) UInt64 memoryCapacity;
// Preferred memory
@property (nonatomic.assign) UInt64 preferredMemoryUsageAfterPurge;
// Current memory usage
@property (nonatomic.assign.readonly) UInt64 memoryUsage;
// init
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity;
Copy the code

Properties are easy to understand. How these properties are used depends on the implementation:

@interface AFAutoPurgingImageCache(a)
// A dictionary to manage cached images
@property (nonatomic.strong) NSMutableDictionary <NSString* , AFCachedImage*> *cachedImages;
// Memory currently in use
@property (nonatomic.assign) UInt64 currentMemoryUsage;
// Secure the queue
@property (nonatomic.strong) dispatch_queue_t synchronizationQueue;
@end
Copy the code

The key of this dictionary is the identifier of the image and the value is the AFCachedImage object. For this object, let’s look at its implementation:

@interface AFCachedImage : NSObject
@property (nonatomic.strong) UIImage *image;//持有image
@property (nonatomic.copy) NSString *identifier;// Unique identifier
@property (nonatomic.assign) UInt64 totalBytes;// The total number of bytes used by the image
@property (nonatomic.strong) NSDate *lastAccessDate;// The time of the last fetch
@property (nonatomic.assign) UInt64 currentMemoryUsage;// Memory currently in use
@end

@implementation AFCachedImage
- (instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier {
    if (self = [self init]) {
        self.image = image;
        self.identifier = identifier;
        // Count the total number of bytes of the current image
        CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
        CGFloat bytesPerPixel = 4.0;
        CGFloat bytesPerSize = imageSize.width * imageSize.height;
        self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize;
        self.lastAccessDate = [NSDate date];
    }
    return self;
}

- (UIImage *)accessImage {
    // The time is refreshed every time the image is retrieved
    self.lastAccessDate = [NSDate date];
    return self.image;
}
@end
Copy the code

You can seeAFCachedImageClass equivalent to pairUIImageWrapped it up, added some attributes that identify the class, and now we’re backAFAutoPurgingImageCacheRead on to see how it is implemented: The above isAFImage cache, the general process is

AFImageDownloader

Download the image class. This class will download the image task in parallel, and the downloaded image will be cached in memory. The most common method we use is to set up images for UIimageView, which also needs to use THE AF UIimageView +AFNetworking classification. For example [cell imageView setImageWithURL: [NSURL URLWithString: model. The imageUrl]].

This time will comeAFtheUIImageView+AFNetworkingClassification ofsetImageWithURL:methodswhyAFImageDownloader *downloader = [[self class] sharedImageDownloader];usingsharedImageDownloader? It means all my downloads can be downloaded from a single downloader. This downloader is associated withAFClass, so set it with association properties.A download credential is then initializedAFImageDownloadReceipt *receipt;Download credentials will be calleddownloadImageForURLRequest:methodsTo sort out the general logic, when we need to load the network picture, we calldownloadImageForURLRequestMethod to create a task and generatereceiptReturn it to us so we can cancel at any time. During the loading process, the user preferentially selects whether to search in the cache according to the cache policy. After the request is successful, the current image will be stored in the memory for next use.

AFThe general process of downloading images is as follows

Some good code details for AF

After a careful look at the code, I found that common OC basic knowledge has specific application in AF, and most of them are also the interview questions. Here I also make a record and sort out.

  • How to create a singleton

throughdispatch_onceTo ensure that only one instance is created when a multithreaded call is made.

  • Dispatch_sync and dispatch_barrier_sync work together to solve parallel read and write problems

GCDusebarrierTo deal withParallel reading serial writingThe specific usage of the question

  • WeakSelf and strongSelf usage — strong and weak dance together

Must know must know,weakSelfAvoid circular references,strongSelfensureblockInternal execution processselfWill not be released.

Possible interview sites for AFNetworking

As mentioned above, when reading the open source library, you should think more and ask more questions. Here are also some interview questions to comb through

AFNetworking 2.x how to start resident child thread? Why do WE need resident child threads?

Af2. x has a detail that starts a resident child thread with runloop. The code looks like this:

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool{[[NSThread currentThread] setName:@"AFNetworking"];

        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; }} + (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });

    return _networkRequestThread;
}
Copy the code

First, why do we want to start resident child threads?

The NSURLConnection interface is asynchronous and then initiates a thread callback. A child thread, on the other hand, usually exits after the synchronous code is executed. In order to receive a callback from NSURLConnection, the child thread must survive at least until the time of the callback. The reason AF keeps the thread resident is that when multiple HTTP requests are made, the child thread will be called back to, so it simply stays alive.

Under what circumstances can a child thread continue to live? This brings us to the second question, how AF starts resident threads, which is really looking at the basics of Runloop.

For runloop, check out my iOS Hack at ⑲: Memory Management and NSRunLoop. To put it simply, the runloop does not exit when it finds a source/timer/observer. So AF adds an NSMachPort to the current runloop. This port actually adds a source event source, so that the thread’s runloop stays in a loop waiting for another thread to send a message to the port. In fact, AF has no message sent to this port.

In addition to the AF approach, Apple actually provides a callback thread solution:

// NSURLConnection
- (void)setDelegateQueue:(nullable NSOperationQueue*) queue

// NSURLSession
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
Copy the code

Apple provides interfaces that allow you to make oneoperationQueueFor callback executionAF3.xAt the beginning of the version, one is created directlyThe concurrency is 1To process the callback.

Extend one:

The interviewer may ask you: Why did you start fromAF3.xYou need to set it upmaxConcurrentOperationCount = 1And theAF2.xDon’t need it?

This is not a difficult question, but it can help the interviewer determine whether the candidate has really studied the source code for the two large versions of AF. Answer: Different functions: AF3. Start x operationQueue is used for receiving NSURLSessionDelegate callback, in view of some multi-threaded data access security considerations, set the maxConcurrentOperationCount = 1 to achieve the effect of serial callback. Af2. x’s operationQueue is used to add operations and make concurrent requests, so do not set it to 1.

The relationship between AFURLSessionManager and NSURLSession. Do I need to create a new Mananger every time?

If we look closely at the code, we should be able to conclude that the manager and session have a one-to-one relationship, and AF will create the corresponding NSURLSession when the Manager is initialized.

So reusing manager is actually reusing session, and what benefits can reusing session bring?

Session support for http2.0 has been around since iOS9. One of the features of http2.0 is multiplexing. Reusing sessions takes advantage of the multiplexing features of HTTP2.0 to reduce the time and resources required to re-establish TCP connections when accessing the same server.

The official document also recommends using different sessions for different functional scenarios. For example, one session handles normal requests and one session handles background requests. One session handles requests made public by the browser, one session handles privacy requests and so on.

How does AFSecurityPolicy avoid man-in-the-middle attacks?

Now, because of the Apple ATS policy, basically cut to HTTPS, the basic principle of HTTPS still need to understand, not here, you can Google related articles.

In general, the first thing we need to understand is a man-in-the-middle attack, basically a hacker intercepts the certificate returned by the server and forgeries his or her own certificate. Often tools like Charles/Fiddler that we use can actually be considered a man-in-the-middle attack.

The solution is actually quite simple, the AFSSLPinningMode of SSL tunneling.AFSecurityPolicy is the related setup.

SSL PinningIs that you need to package the server’s public key into the client,tlsDuring authentication, the server certificate is compared with the local certificate. If the certificate is the same, authentication is allowed.Because a digital certificate has a validity period, authentication may fail after it is invalidated after being embedded in the client. Therefore, you can set it toAFSSLPinningModePublicKeyIn this case, as long as the certificate is renewed, the public key in the certificate does not change, can pass the authentication.

Write in the back

Study harmoniously without being impatient. I’m still me, a different color of fireworks.