你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Xcode 代碼注釋自動縮進插件:開發教程及源碼

Xcode 代碼注釋自動縮進插件:開發教程及源碼

編輯:IOS開發基礎

1.jpg

自從公司啟用代碼review以來,每個開發者的代碼風格漸漸保持一致了,看到規范統一的代碼就是覺得比較舒服。

不過Xcode的代碼注釋功能一直用著很別扭,比如下面的例子:

//注釋前:
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
}
//注釋後:
- (void)viewDidLoad
{
    [super viewDidLoad];
//    self.view.backgroundColor = [UIColor whiteColor];
}
//注釋前:
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    self.view.backgroundColor = [UIColor whiteColor];
}
 
//注釋後:
- (void)viewDidLoad
{
    [super viewDidLoad];
 
//    self.view.backgroundColor = [UIColor whiteColor];
}

可以看到,Xcode只是在行開頭加上注釋符,注釋後的代碼縮進對不齊,很難看。這對患有神經感官歇斯底裡毛細血管穿梭圖魯西斯症候群(簡稱強迫症)的人來說,是一件非常痛苦的事情。

在stackoverflow搜了一下,發現有人也提了這個問題,然後有個人回答說:

  1. 你先按command + [把代碼往左縮進到最前面

  2. 再按command + /注釋代碼

  3. 最後按command + ]把代碼往右縮進

結果這條回答被采納為最差答案。

所以我只能自己寫個插件來讓注釋自動縮進了,首先新建一個Xcode插件工程,比較詳細的創建過程可以見《Xcode插件AllTargets開發教程》。

一開始要先整理一下寫插件的思路,我們使用快捷鍵command + /的時候,Xcode肯定會調用一個方法來注釋代碼,這個方法實現的功能應該是在代碼行最前面加上//注釋符。

而當我們寫的代碼有縮進的話,縮進的地方都是空格,所以我們可以hook注釋的方法,把注釋符改為插入到第一個非空格字符前就行了。

接下來就開始查找代碼注釋的方法了,首先在Xcode的私有類頭文件裡搜索comment,可以發現一個比較可疑的方法:- (void)commentAndUncommentCurrentLines:(id)arg1;,hook這個方法,把參數arg1打印出來,參考代碼如下:

#import "DVTSourceTextView+Hook.h"
@implementation DVTSourceTextView (Hook)
+ (void)hook
{
    [self jr_swizzleMethod:@selector(commentAndUncommentCurrentLines:)
                withMethod:@selector(hook_commentAndUncommentCurrentLines:)
                     error:nil];
}
- (void)hook_commentAndUncommentCurrentLines:(id)arg1
{
    NSLog(@"%@", arg1);
    [self hook_commentAndUncommentCurrentLines:arg1];
}
@end
#import "DVTSourceTextView+Hook.h"
 
@implementation DVTSourceTextView (Hook)
 
+ (void)hook
{
    [self jr_swizzleMethod:@selector(commentAndUncommentCurrentLines:)
                withMethod:@selector(hook_commentAndUncommentCurrentLines:)
                     error:nil];
}
 
- (void)hook_commentAndUncommentCurrentLines:(id)arg1
{
    NSLog(@"%@", arg1);
    [self hook_commentAndUncommentCurrentLines:arg1];
}
 
@end

使用command + /對代碼進行注釋和反注釋,輸出結果為:


由此可知,使用快捷鍵注釋,實際上是調用了Xcode菜單欄點擊的方法。而當我們注釋代碼時,會有log打印,也說明找對了方法。

commentAndUncommentCurrentLines:這個方法位於DVTSourceTextView類中,而這個類位於DVTKit.framework裡,接下來打開反匯編軟件Hopper Disassembler,把DVTKit.framework拖進去進行反匯編。

搜索並選中commentAndUncommentCurrentLines:方法,按alt + enter生成偽代碼,由於代碼太長所以只貼出關鍵代碼:

void -[DVTSourceTextView commentAndUncommentCurrentLines:](void * self, void * _cmd, void * arg2)
{
    //......
    rax = (r12)(r15, @selector(stringByTogglingCommentsInLineRange:), rdx, r13);
    //......
}
void -[DVTSourceTextView commentAndUncommentCurrentLines:](void * self, void * _cmd, void * arg2)
{
    //......
 
    rax = (r12)(r15, @selector(stringByTogglingCommentsInLineRange:), rdx, r13);
 
    //......
}

由上面的代碼可知,stringByTogglingCommentsInLineRange:方法返回了注釋或反注釋後的字符串,接著再搜索並查看該方法的偽代碼:

void * -[DVTSourceLanguageService stringByTogglingCommentsInLineRange:](void * self, void * _cmd, struct _NSRange arg2) 
{
    //......
    if (var_70 == 0x2) {
        rbx = [[rdi stringByUncommentingString:r15] retain];
    } else {
        rbx = [[rdi stringByCommentingString:r15] retain];
    }
    //......
}
void * -[DVTSourceLanguageService stringByTogglingCommentsInLineRange:](void * self, void * _cmd, struct _NSRange arg2) 
{
    //......
 
    if (var_70 == 0x2) {
        rbx = [[rdi stringByUncommentingString:r15] retain];
    } else {
        rbx = [[rdi stringByCommentingString:r15] retain];
    }
 
    //......
}

同理,繼續查看stringByCommentingString:方法的偽代碼:

void * -[DVTSourceLanguageService stringByCommentingString:](void * self, void * _cmd, void * arg2)
{
    rdx = arg2;
    r15 = self;
    r12 = [rdx retain];
    r14 = *objc_msgSend;
    rbx = [[r15 lineCommentPrefixes] retain];
    r13 = [[rbx firstObject] retain];
    [rbx release];
    if ([r13 length] != 0x0) {
            r15 = r13;
    }
    else {
            r14 = *objc_msgSend;
            var_30 = r12;
            rbx = [[r15 blockCommentCircumfixes] retain];
            r12 = [[rbx firstObject] retain];
            r15 = *objc_release;
            [rbx release];
            rbx = r15;
            r15 = [[r12 firstObject] retain];
            [r13 release];
            r13 = [[r12 lastObject] retain];
            [r12 release];
            if (r13 != 0x0) {
                    r12 = [[var_30 stringByAppendingString:r13] retain];
                    (rbx)(var_30, @selector(stringByAppendingString:));
                    (rbx)(r13, @selector(stringByAppendingString:));
            }
            else {
                    r12 = var_30;
            }
    }
    rbx = [[r15 stringByAppendingString:r12] retain];
    r14 = *objc_release;
    [r12 release];
    [r15 release];
    rdi = rbx;
    rax = [rdi autorelease];
    return rax;
}
void * -[DVTSourceLanguageService stringByCommentingString:](void * self, void * _cmd, void * arg2)
{
    rdx = arg2;
    r15 = self;
    r12 = [rdx retain];
    r14 = *objc_msgSend;
    rbx = [[r15 lineCommentPrefixes] retain];
    r13 = [[rbx firstObject] retain];
    [rbx release];
    if ([r13 length] != 0x0) {
            r15 = r13;
    }
    else {
            r14 = *objc_msgSend;
            var_30 = r12;
            rbx = [[r15 blockCommentCircumfixes] retain];
            r12 = [[rbx firstObject] retain];
            r15 = *objc_release;
            [rbx release];
            rbx = r15;
            r15 = [[r12 firstObject] retain];
            [r13 release];
            r13 = [[r12 lastObject] retain];
            [r12 release];
            if (r13 != 0x0) {
                    r12 = [[var_30 stringByAppendingString:r13] retain];
                    (rbx)(var_30, @selector(stringByAppendingString:));
                    (rbx)(r13, @selector(stringByAppendingString:));
            }
            else {
                    r12 = var_30;
            }
    }
    rbx = [[r15 stringByAppendingString:r12] retain];
    r14 = *objc_release;
    [r12 release];
    [r15 release];
    rdi = rbx;
    rax = [rdi autorelease];
    return rax;
}

這個方法的作用是把待注釋的代碼加上注釋符進行注釋,這也是Xcode注釋代碼的具體實現方法,將偽代碼翻譯成OC代碼為:

- (NSString *)stringByCommentingString:(NSString *)commentingString
{
    NSString *lineCommentPrefix = [self.lineCommentPrefixes firstObject];
    if (lineCommentPrefix.length == 0) {
        // 使用 `/* */` 注釋塊
        NSArray *blockCommentCircumfix = [self.blockCommentCircumfixes firstObject];
        lineCommentPrefix = [blockCommentCircumfix firstObject];
        NSString *lineCommentSuffix = [blockCommentCircumfix lastObject];
        if (lineCommentSuffix) {
            commentingString = [commentingString stringByAppendingString:lineCommentSuffix];
        }
    }
    //在待注釋的代碼最前面加上注釋符
    return [lineCommentPrefix stringByAppendingString:commentingString];
}
- (NSString *)stringByCommentingString:(NSString *)commentingString
{
    NSString *lineCommentPrefix = [self.lineCommentPrefixes firstObject];
 
    if (lineCommentPrefix.length == 0) {
 
        // 使用 `/* */` 注釋塊
        NSArray *blockCommentCircumfix = [self.blockCommentCircumfixes firstObject];
        lineCommentPrefix = [blockCommentCircumfix firstObject];
 
        NSString *lineCommentSuffix = [blockCommentCircumfix lastObject];
        if (lineCommentSuffix) {
            commentingString = [commentingString stringByAppendingString:lineCommentSuffix];
        }
    }
 
    //在待注釋的代碼最前面加上注釋符
    return [lineCommentPrefix stringByAppendingString:commentingString];
}

因此我們可以hook這個方法,在第一個非空格字符的位置插入注釋符。

如果某一行代碼是空行的話,就不需要注釋了,我看了大部分的編輯器都是這麼處理的。

參考代碼如下:

- (NSString *)hook_stringByCommentingString:(NSString *)commentingString
{
    NSString *lineCommentPrefix = [self.lineCommentPrefixes firstObject];
    if (lineCommentPrefix.length == 0) {
        // 使用 `/* */` 注釋塊
        NSArray *blockCommentCircumfix = [self.blockCommentCircumfixes firstObject];
        lineCommentPrefix = [blockCommentCircumfix firstObject];
        NSString *lineCommentSuffix = [blockCommentCircumfix lastObject];
        if (lineCommentSuffix) {
            commentingString = [commentingString stringByAppendingString:lineCommentSuffix];
        }
    }
    // 判斷代碼是否含有非空格字符
    NSRange range = [commentingString rangeOfString:@"[^\\s]" options:NSRegularExpressionSearch];
    if (range.location == NSNotFound) {
        // 如果沒有非空格字符,就不注釋
        return commentingString;
    }
    // 在第一個非空格字符的位置插入注釋符
    NSMutableString *mutableString = [commentingString mutableCopy];
    [mutableString insertString:lineCommentPrefix atIndex:range.location];
    return [mutableString copy];
}
- (NSString *)hook_stringByCommentingString:(NSString *)commentingString
{
    NSString *lineCommentPrefix = [self.lineCommentPrefixes firstObject];
 
    if (lineCommentPrefix.length == 0) {
 
        // 使用 `/* */` 注釋塊
        NSArray *blockCommentCircumfix = [self.blockCommentCircumfixes firstObject];
        lineCommentPrefix = [blockCommentCircumfix firstObject];
 
        NSString *lineCommentSuffix = [blockCommentCircumfix lastObject];
        if (lineCommentSuffix) {
            commentingString = [commentingString stringByAppendingString:lineCommentSuffix];
        }
    }
 
    // 判斷代碼是否含有非空格字符
    NSRange range = [commentingString rangeOfString:@"[^\\s]" options:NSRegularExpressionSearch];
    if (range.location == NSNotFound) {
        // 如果沒有非空格字符,就不注釋
        return commentingString;
    }
 
    // 在第一個非空格字符的位置插入注釋符
    NSMutableString *mutableString = [commentingString mutableCopy];
    [mutableString insertString:lineCommentPrefix atIndex:range.location];
    return [mutableString copy];
}

插件下載地址為:https://github.com/poboke/IndentComments

安裝插件後,使用效果如下所示:

1.gif

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved