5

Nó không phải là lạ khi nhìn thấy lời khuyên phát triển iOS dọc theo dòng: các khối đặt tên trong mục tiêu-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__); 
    }); 
}); 

này là rất tốt, nhưng nó có thể được khó khăn để gỡ rối khi họ gặp khó khăn. Nhìn vào kết quả đầu ra:

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

Dòng log đầu tiên có tên lớp và phương thức để chúng tôi hy vọng theo dõi vấn đề ở khối ngoài (hy vọng chúng tôi chưa xác định nhiều khối trong phương pháp đó), nhưng dòng đăng nhập thứ hai (từ khối bên trong)? Chúc may mắn, đặc biệt nếu bạn đã sử dụng mẫu này rất nhiều trong ứng dụng của mình.

Có cách nào để đặt tên cho các khối sẽ giúp chúng tôi xác định vị trí nguồn của chúng trong đầu ra bàn điều khiển và nhật ký sự cố không?

+1

Ngoài ra, bạn có thể đặt điểm ngắt trong khối và trình gỡ lỗi sẽ phun vào bạn một dấu vết ngăn xếp khi nó đạt đến (IME hữu ích hơn 'NSLog' trong hầu hết các trường hợp). –

+0

Tôi cũng sử dụng điểm ngắt và không bao giờ cảm thấy bất lợi bởi các khối. – danh

+3

Tôi không nghĩ rằng có được xây dựng trong đặt tên của khối, nhưng bạn chắc chắn có thể tên hàng đợi, mà được bạn một phần của con đường. Điều đó xuất hiện trong trình gỡ lỗi và bạn có thể bao gồm điều đó trong câu lệnh NSLog. Ngoài ra, bạn cũng có thể sử dụng '__FILE__' và' __LINE__'. Có lẽ xác định macro của riêng bạn mà là một sự kết hợp của các bên trên. – Rob

Trả lời

0

Một điều bạn có thể làm là sử dụng các chức năng thay vì các khối và dispatch_async_f thay vì dispatch_async. Sự cân bằng là đáng kể, mặc dù, bạn mất bản chất mã nội tuyến của các khối, và khả năng nắm bắt trạng thái mà không cần phải sắp xếp nó thông qua một con trỏ ngữ cảnh.

Bạn cũng có thể chỉ cần khai báo khối bên trong bên ngoài khối ngoài và chặn nó trong biến cục bộ. Một chút ngắn gọn hơn, nhưng nó sẽ gắn thẻ nó với tên của phương thức kèm theo như khối ngoài.

4

Các khối, sau khi được sao chép, trở thành các phiên bản của NSBlock, có nghĩa là chúng ta có thể sử dụng thời gian chạy để thêm tất cả các loại điều tốt đẹp vào nó. Dưới đây là một ví dụ:

@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; 
} 

Dưới đây là kết quả của chương trình này:

// 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> 

tôi sẽ đề nghị bạn nên quấn chức năng beautifyBlockDescription trong một macro để cho mã phát hành nó chỉ trả về khối.

+0

Có lẽ quá phức tạp nhưng rất thú vị! – Sulthan

+0

Đó là một chút phức tạp nhưng chơi xung quanh với thời gian chạy là không bao giờ thực sự đơn giản. Đây cũng là loại hàm phù hợp độc đáo trong một thư viện nhỏ được sử dụng lại nhiều lần ... – aLevelOfIndirection

0

Bạn có thể thử sử dụng https://github.com/conradev/BlockTypeDescription để cải thiện khả năng đọc nhật ký trong khi phát triển. Tuy nhiên, thư viện này thay thế một phương thức trên lớp NSBlock riêng, bạn không bao giờ nên thử sử dụng nó trong một nhị phân appstore hoặc nó có thể sẽ bị từ chối. Điều đó có nghĩa là bạn không thể cải thiện các sự cố mà bạn nhận được từ Apple.