The original article start my blog: blog.cocosdever.com/2019/07/25/…
What’s New
- This page was last updated on July 25, 2019
- First updated on July 25, 2019
preface
In the world of programming, design patterns are often heard and rarely done. Many programmers have heard of design mode, but it is rarely their own manual implementation of some real sense of design mode, just a few days in the review of design mode, and then today saw iOS popular with flexible, high scalability of the open source logging framework CocoaLumberjack source code, feel, Now I’d like to talk about my insights and uncover, step by step, how this highly flexible framework is designed.
The use of CocoaLumberjack
Let’s take a quick look at CocoaLumberjack. CocoaLumberjack provides users with several log modes, console, system log, sandbox log, in addition to the indispensable log formatting function Formatter. The following example takes the most complex sandbox log:
// You need to define this variable to determine what level of logging is actually required.
static const DDLogLevel ddLogLevel = DDLogLevelDebug;
// Create a file log to define the configuration information
DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // File Logger
fileLogger.logFormatter = [[DDDispatchQueueLogFormatter alloc] init];
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
// Add a Logger
[DDLog addLogger:fileLogger];
// You can add more than one console logger at a time. For example, add a console logger, so that both the file and the console have log output
DDTTYLogger *log2 = [DDTTYLogger sharedInstance];
log2.logFormatter = [[CCLogFormatter alloc] init];
[DDLog addLogger:log2];
// Use the built-in macro directly to start logging
DDLogDebug(@"Debug");
Copy the code
The above simple code, completed the log format customization, log output customization, log sandbox file configuration and other functions, flexible Settings. In addition, it also demonstrates the user extended formatting class, used to format the output content, such a flexible design, is really appreciated, let’s take a look at the source code, this is how to achieve.
CocoaLumberjack source analysis
After expanding the macro to get the following real execution entry, the following main analysis of the sandbox log source
[DDLog
log:NO
level:ddLogLevel
flag:DDLogFlagDebug
context:0
file:__FILE__
function:__FUNCTION__
line:__LINE__
tag:nil
format:@"Debug"
];
Copy the code
The above class method is called layer by layer until it reaches the following code:
// DDLog.m
// Encapsulate the parameter into
[self.sharedInstance log:asynchronous message:message level:level flag:flag context:context file:file function:function line:line tag:tag];
Copy the code
This is the first design pattern encountered, and the most common, singletons, nothing to say, so let’s move on:
// DDLog.m
// The program starts calling the following methods to enter the initialization phase
- (void)queueLogMessage:(DDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag {
// omit other code
[self lt_log:logMessage];
}
Copy the code
The program executes the LT_log method, passes the message in, and continues:
// DDLog.m
- (void)lt_log:(DDLogMessage *)logMessage {
// omit other code
for (DDLoggerNode *loggerNode in self._loggers) {
// skip the loggers that shouldn't write this message based on the log level
// Retrieve all stored Logger nodes from the current DDLog instance, which contains the Logger object I passed in
if(! (logMessage->_flag & loggerNode->_level)) {continue;
}
dispatch_group_async(_loggingGroup, loggerNode->_loggerQueue, ^{ @autoreleasepool{ [loggerNode->_logger logMessage:logMessage]; }}); }}Copy the code
The _logger is of type ID
// DDFileLogger.m
// The Logger analyzed here is a sandbox log
- (void)logMessage:(DDLogMessage *)logMessage {
// omit other code
// Here you can see that the framework starts using the Formatter object I passed in
if(_logFormatter ! =nil) { message = [_logFormatter formatLogMessage:logMessage]; isFormatted = message ! = logMessage->_message; } [self lt_logData:[message dataUsingEncoding:NSUTF8StringEncoding]];
}
Copy the code
Note that the _logFormatter variable is of type ID
The bridge model
I don’t care about 100% to tell you that CocoaLumberjack mainly uses bridge mode to achieve extensible and highly flexible functions, because there are several design patterns like this, and I’m not writing a paper analysis, let’s call it bridge mode. In fact, bridge mode is similar to strategy mode. Policy patterns are more about representing behavioral algorithms, and more about providing simple stateless algorithm functionality when classes that themselves implement a policy are aggregated into the main class. As the CocoaLumberjack framework can see, classes like Formatter and Logger themselves contain more complex logic and internal object properties, so I think it’s more appropriate to call them bridging modes. Specific definition can be their own Google search, he is long is the article above the analysis of that, here I quote the novice tutorial inside the description of the bridge mode, I think it is appropriate. That’s what it looks like. It’s wonderful. Read the source code, the following to customize the output format, the whole process almost does not need to search any information online, self-sufficient.
Extension CocoaLumberjack
As long as my class implements the DDLogFormatter protocol, I can configure DDLog as a valid Formatter object. Here’s the source for the extended Formatter:
//
// CCLogFormatter.h
// OCSimpleView
//
// Customize the formatting information for the CocoaLumberjack framework
// Created by Cocos on 2019/7/25.
// Copyright © 2019 Cocos. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CocoaLumberjack/CocoaLumberjack.h>
NS_ASSUME_NONNULL_BEGIN
@interface CCLogFormatter : NSObject <DDLogFormatter>
/** * Default initializer */
- (instancetype)init;
/** * Designated initializer, requires a date formatter */
- (instancetype)initWithDateFormatter:(NSDateFormatter * __nullable)dateFormatter NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
Copy the code
//
// CCLogFormatter.m
// OCSimpleView
//
// Created by Cocos on 2019/7/25.
// Copyright © 2019 Cocos. All rights reserved.
//
#import "CCLogFormatter.h"
@interface CCLogFormatter(a){
NSDateFormatter *_dateFormatter;
}
@end
@implementation CCLogFormatter
- (instancetype)init {
return [self initWithDateFormatter:nil];
}
- (instancetype)initWithDateFormatter:(NSDateFormatter * __nullable)aDateFormatter {
if ((self = [super init])) {
if (aDateFormatter) {
_dateFormatter = aDateFormatter;
} else {
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; / / 10.4 + style
[_dateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"]; }}return self;
}
- (NSString * _Nullable)formatLogMessage:(nonnull DDLogMessage *)logMessage {
NSString *dateAndTime = [_dateFormatter stringFromDate:(logMessage->_timestamp)];
return [[NSString alloc] initWithFormat:@"[%@]\n%@:\n[%@]%@", logMessage.file, logMessage.function, dateAndTime, logMessage.message];
}
@end
Copy the code
Actually very simple, I realized the formatLogMessage: this method, the other code directly from DDLogFileFormatterDefault class copy to come over, Of course, that is directly make CCLogFormatter inherit DDLogFileFormatterDefault initialization code here can be omitted. In CCLogFormatter I change the format from the original:
2019/07/25 14:33:36:239 Debug
Copy the code
In the following format:
[~/Xcode/Study/OCSimpleView/OCSimpleView/ViewController.m]
-[ViewController loggerFunc]:
[2019/07/25 14:58:07:358]Debug
Copy the code
That’s the end of the extension. Other more complex functions, which are constantly changing, follow the same pattern.
conclusion
This article is mainly through the popular framework CocoaLumberjack source code analysis, find out the design patterns, and then understand the importance of design patterns, learn one of the flexible extension framework implementation methods. It is said that there are more than 30 design patterns in total, which is too many to learn. However, I think it is the most important to keep a young heart and live to learn.