5

的情況並不少見,看看iOS開發建議沿着線: 在objective-c中命名塊?

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // work in background 
    NSLog(@"%s work", __PRETTY_FUNCTION__); 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     // update UI on main queue 
     NSLog(@"%s updateUI", __PRETTY_FUNCTION__); 
    }); 
}); 

這是偉大的,但出問題的時候可能會很難調試。縱觀輸出:

AppName[1051:4013] __47-[Classname methodName]_block_invoke_0 work 
AppName[1051:907] __block_global_0 updateUI 

第一個日誌行具有的類和方法的名稱,以便我們跟蹤外塊的問題(希望我們沒有在這個方法定義了很多塊)的一些希望,但第二個日誌行(從內部塊)?祝你好運,特別是如果你已經在你的應用中使用過這種模式。

有沒有辦法給塊名稱,這將有助於我們在控制檯輸出和崩潰日誌中識別它們的源位置?

+1

或者,您可以在塊中設置一個斷點,並且調試器在到達時會向您發送一個堆棧跟蹤(在大多數情況下,IME比'NSLog'更有幫助)。 – 2013-01-01 02:37:41

+0

我也使用斷點,並且從未感受到塊的劣勢。 – danh

+3

我不認爲在命名模塊中有內置命令,但是你肯定可以命名隊列,這可以讓你成爲隊列的一部分。這顯示在調試器中,您可以將其包含在NSLog語句中。或者,您也可以使用'__FILE__'和'__LINE__'。也許定義你自己的宏是上述的組合。 – Rob

回答

0

你可以做的一件事是使用功能,而不是塊,dispatch_async_f,而不是dispatch_async。但是,折中很重要,因爲您失去了塊的內聯代碼性質,並且無需通過上下文指針封送狀態即可捕獲狀態。

你也可以在外部塊之外聲明內部塊並將其存儲在局部變量中。不太簡潔,但它會用外部塊的封閉方法名稱來標記它。

4

塊被複制後成爲NSBlock的實例,這意味着我們可以使用運行時向它添加各種好東西。這裏有一個例子:

@protocol QuietTheCompiler<NSObject> 
- (NSString*) prettyBlockDescription; 
@end 

static id beautifyBlockDescription(id block, NSString *name) 
{ 
    static void *kAssocObjectPrettyBlockDescription = "A"; 
    Class blockClass = [block class]; 

    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 

     SEL descrSel  = @selector(description); 
     SEL prettySel = @selector(prettyBlockDescription); 
     Method descrMethod = class_getInstanceMethod(blockClass, descrSel); 
     IMP originalImpl = class_getMethodImplementation(blockClass, descrSel); 

     IMP prettyImpl = imp_implementationWithBlock(^(id self_) { 
      id value = objc_getAssociatedObject(self_, kAssocObjectPrettyBlockDescription); 
      return (value != nil)? value : originalImpl(self_, descrSel); 
     }); 

     if (class_addMethod(blockClass, prettySel, prettyImpl, method_getTypeEncoding(descrMethod))) { 
      IMP newImpl = imp_implementationWithBlock(^(id self_) { 
       return [self_ prettyBlockDescription]; 
      }); 
      class_replaceMethod(blockClass, descrSel, newImpl, method_getTypeEncoding(descrMethod)); 
     } 
    }); 

    NSString *description = [NSString stringWithFormat:@"<%@: %p name=%@>",NSStringFromClass(blockClass),block,name]; 
    objc_setAssociatedObject(block, kAssocObjectPrettyBlockDescription, description, OBJC_ASSOCIATION_RETAIN); 

    return block; 
} 


int main (int argc, const char * argv[]) 
{ 
    @autoreleasepool { 

     int (^block1)(int,NSString*) = ^(int i, NSString *fmt) { 
      return i; 
     }; 

     id blockObject1 = beautifyBlockDescription([block1 copy], @"Hello"); 

     int (^block2)(int,NSString*) = ^(int i, NSString *fmt) { 
      return i+1; 
     }; 

     id blockObject2 = [block2 copy]; 

     NSLog(@"Block 1: %@", blockObject1); 
     NSLog(@"Block 1: %@", blockObject2); 
    } 
    return 0; 
} 

下面是該程序的輸出:

// Sample Output 
2013-03-31 12:34:48.984 Dummy[1231:303] Block 1: <__NSGlobalBlock__: 0x1000059d0 name=Hello> 
2013-03-31 12:34:48.987 Dummy[1231:303] Block 1: <__NSGlobalBlock__: 0x100005a10> 

我建議你換行beautifyBlockDescription功能的宏,以便爲發佈代碼,它只是返回塊。

+0

可能太複雜但很有趣! – Sulthan

+0

這有點複雜,但是運行時並不是很簡單。這也是一種很好地適合於一個小庫中的函數,它可以反覆使用...... – aLevelOfIndirection

0

您可以嘗試使用https://github.com/conradev/BlockTypeDescription來提高開發過程中日誌的可讀性。但是,該庫替代了私有NSBlock類中的方法,因此您絕不應該嘗試在AppStore二進制文件中使用它,否則它可能會被拒絕。這意味着你無法改進從Apple獲得的crashlog。