Project making

The starting point

In today’s work, I wrote a utility class. After I completed all the functions in.m, I realized that copying all the interfaces from.m to.h was too cumbersome, so I decided to write a command line tool to do the work.

The results you want to achieve

We designed the gadget to run directly in the terminal, passing in a.m file path parameter, and printing out all the method names in it.


> fti PWFileController.m 


- (NSString *)bytesToAvaiUnit:(long long)bytes;
- (long long) fileSizeAtPath:(NSString*) filePath;
- (long long) folderSizeAtPath:(NSString*) folderPath;
- (void) clearFolderAtPath:(NSString*) folderPath;
- (float)getTotalDiskSpace;
- (NSString *)getHomeDirectory;


The first step is to create a new Mac Command Line Tool project. This project has only one main.m file and contains the following contents

#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!" ); } return 0; }

C function methods in M files do not have automatic memory pools, so to use ObjC code in C methods, we must use @AutoReleasePool curly braces to ensure that the stack memory is freed after the end of C method.

Second, the argc argument in main represents the number of arguments on the command line, and argv, a char array, is the contents of each argument.

So we first determine the number of argc. Note that the command in the shell itself takes one argument bit, so if there are no arguments, argc should be 1.

if(argc<=1) return 0; // Exit the program directly when argc<=1

Then we’re going to get the second argument from the command line, which is the path to the.m file

NSString* filePath = [[NSString alloc] initWithCString:argv[1] encoding:NSUTF8StringEncoding];

If the file does not exist, terminate the program

if(! [[NSFileManager defaultManager] fileExistsAtPath: filePath]) {NSLog (@ "file does not exist"); return 0; }

We then declare a method to find the interface before the main function, which is formatted as a C method

NSArray* findInterface (NSString* text);

And then implement it, be sure to say @AutoReleasePool

NSArray* findInterface (NSString* text) { @autoreleasepool { NSString *regex = @"-\\s? \ \ (. *? \ \). *? (? =\\n|$|\\{)"; NSString *str = text; NSError *error; NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:&error]; NSArray *matches = [regular Matchesinstring: STR options:0 range:NSMakeRange(0, STR. Length)]; NSMutableArray* result = [NSMutableArray arrayWithCapacity:matches.count]; For (nsTextCheckingResult *match in matches) {nsRange range = [match range]; NSString *mStr = [str substringWithRange:range]; [result addObject:mStr]; } return [result copy]; }}

There is nothing special about this regular expression search. The use of the regular expression of the class NSRegularExpression is relatively simple, just refer to the code above, so I will briefly describe the matching rules of the regular

-\\s? \ \ (. *? \ \). *? (? =\\n|$|\\{)

Begin with a -, a number of Spaces between the first open bracket, then a number of Spaces and characters, then a close bracket, then a number of Spaces and characters, ending with three matches, \n, $at the end of the string, and the open curly brace {

This way we read the contents of the file in the main method and then call this method to print all the interface names.

NSString* s = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; NSString* f = [[findInterface(s) componentsJoinedByString:@";\n"] stringByAppendingString:@";"] ; NSLog(@"result:\n%@",f);

See the project GitHub for the complete code


The released version of the project can be released from the menu product-> Archive, and then copied to /usr/local/bin for use in terminal.

Note that I have simplified the program name of the Archive to FTI for convenience.


Class methods match, the regular – instead of (- | \ \ +). The newline method can be matched according to {, and the (? = $| | \ \ n \ \ {) instead of (^;) *? (? = \ \ {). The principle of your own analysis.

Since we changed the matching rule, we need to do some processing on the fetched content. In the FindInterface method, we remove the newline character and the retrieved content; StringByReplacingOccurrencesOfString method is used to process

for (NSTextCheckingResult *match in matches) { NSRange range = [match range]; NSString *mStr = [str substringWithRange:range]; mStr = [mStr stringByReplacingOccurrencesOfString:@"\n" withString:@""]; mStr = [mStr stringByReplacingOccurrencesOfString:@";" withString:@""]; mStr = format(mStr); // This method does the following zh [result addObject:mStr]; }

And then we’re going to add a format method to format the multiline function into a standard one-line function.

NSString* format (NSString* string){
    @autoreleasepool {
        string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
        NSArray *components = [string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
        components = [components filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self <> ''"]];
        string = [components componentsJoinedByString:@" "];
        return string;