Original Article:
"https://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html"

ორიგინალური გვერდი https://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html

 

პარასკევი Q&A 2013-05-03: სათანადო გამოყენების ამტკიცებს,

ამტკიცებს არის ძლიერი ინსტრუმენტი, მშენებლობის ხარისხის კოდი, მაგრამ ისინი ხშირად ცუდად ესმოდა. დღეს მინდა განვიხილოთ სხვადასხვა ვარიანტები წერა ამტკიცებს, ქერის პროგრამები და საუკეთესო გზა, რათა გამოყენება, თემის მიერ შემოთავაზებული მკითხველს Ed Wynne.

APIs
ფუნდამენტურად, ამტკიცებენ, არის პირველი ზარი, რომელიც იღებს გამოხატვის და მიუთითებს უკმარისობა რამდენიმე გზა, თუ გამოხატვის თავისუფლება არ არის ჭეშმარიტი. ძირითადი იდეა არის ის, რომ შემოწმება პირობები, რომ ყოველთვის უნდა იყოს ჭეშმარიტი ისე, რომ თქვენ ვერ ადრეული და ცხადია, ვიდრე ვერ მოგვიანებით და confusingly. მაგალითად, მასივი dereference ვერ სხვადასხვა უცნაური გზები თუ თქვენ მისთვის ცუდი ინდექსი:

    x = array[index]; // sure hope index is in range

გამოყენებით ამტკიცებენ, შეგიძლიათ დაეხმაროს მას, აშკარაა, რა გაფუჭდა:

    assert(index >= 0 && index < arrayLength);
    x = array[index];

ეს რეალურად მეტყველებს API ამტკიცებს. C უზრუნველყოფს assert ფუნქცია, თუ თქვენ #include <assert.h>. ის ერთი გამოხატულებაა. თუ გამოხატვის მართალია, იგი არაფერს აკეთებს. თუ ეს სიცრუეა, ეს ნამუშევარი გამოხატვის, ფაილის სახელი და ხაზის ნომერი, სადაც ამტკიცებენ, მდებარეობს, და მაშინ მოუწოდებს abort, შეწყვეტის პროგრამა.

კაკაო უზრუნველყოფს რამდენიმე ამტკიცებენ, ფუნქციები, ასევე. ძირითადი არის NSAssert. იგი იღებს გამოხატვის და სიმებიანი აღწერა, რომელიც შეიძლება იყოს სტრიქონში:

    NSAssert(x != y, @"x and y were equal, this shouldn't happen");
    NSAssert(z > 3, @"z should be greater than 3, but was actually %d", z);
    NSAssert(str != nil, @"nil string while processing %@ of type %@", name, type);

როგორიცაა assert ფუნქცია, ეს ჟურნალი მტკიცება, მარცხი თუ გამოხატვის არის ცრუ. მას შემდეგ აწვდის არის NSInternalInconsistencyException, და რა ხდება მაშინ დამოკიდებულია იმაზე, თუ რა გამონაკლისი ელფოსტის იმყოფებიან. ტიპიური კაკაო app, ეს იქნება ან იქნება დაჭერილი და შესული მიერ runloop, ან ეს იქნება შეწყვიტოს განაცხადი.

სამწუხაროდ, ხე ეხლა NSAssert არის სუსტი. ეს ჟურნალი ფაქტია, რომ მტკიცება ვერ მოხერხდა, ისევე, როგორც მეთოდი, ეს იყო და filename და ონლაინ ნომერი, მაგრამ იგი ფაქტობრივად არ შესვლა იმ გამონათქვამს, რომელიც ჩაიშალა, და არც იმას, რომ ეს შესვლა, მიზეზი string პირობით, რომ მაკროეკონომიკური. გარდა იგი აწვდის არ მოიცავს იმ მიზეზით, string, მინიმუმ, ისე, როგორც ხანგრძლივი, როგორც გამონაკლისი იღებს დაბეჭდილი რაღაც მომენტში, რომ გამოჩნდება.

არსებობს რამდენიმე ვარიანტი ამ დარეკეთ ხელმისაწვდომი კაკაო. “NSAssert მოვუწოდებთ მუშაობს მხოლოდ ერთი მიზანი-C მეთოდი, ასე რომ, არსებობს ექვივალენტი NSCAssertმოვუწოდებთ, რომ მუშაობს C ფუნქცია. არსებობს ასევე NSParameterAssert, რომელიც არ მიიღოს აღწერა სიმებიანი და განკუთვნილია სწრაფად შემოწმების პარამეტრი ღირებულება, და ექვივალენტი NSCParameterAssert C ფუნქციები.

ავაშენოთ თქვენი საკუთარი
ჩაშენებული ვარიანტი არ არის დიდი. C assert არის წესიერი, მაგრამ არ იძლევა დააკონფიგურიროთ გაგზავნა. კაკაო ზარები აქვს ცუდი ხე, და მათი საქციელი იმ შემთხვევაში, ვერ მტკიცება დამოკიდებული ძალიან ბევრი runtime კონტექსტში, და შეიძლება რეალურად არ შეწყვიტოს ოთახი.

ეს ყველაფერი არ არის იმისთვის, რომ ავაშენოთ, თუმცა, მოდით, ავაშენოთ ერთი რომ აკეთებს რამ უფლება! ჩვენ გვინდა, ზარი, რომელიც იღებს გამოხატვის და სურვილისამებრ აღწერა სტრიქონში:

    MAAssert(x > 0);
    MAAssert(y > 3, @"Bad value for y");
    MAAssert(z > 12, @"Bad value for z: %d", z);

ეს უნდა შეხვიდეთ გამოხატვის სტრიქონში, თუ ის არსებობს, და filename, ხაზის ნომერი, და ფუნქციის სახელი, სადაც პრობლემა მოხდა. გარდა ამისა, იგი მხოლოდ უნდა შეაფასოს სტრიქონში პარამეტრების თუ მტკიცება, ვერ, იმისათვის, რომ რამ უფრო ეფექტური. ყველა ამ მოუწოდებს მაკრო.

როგორც ყველა კარგი multi-line macros, ეს მაკრო არის გახვეული do/while მშენებლობა:

        #define MAAssert(expression, ...) \
        do { \

პირველი, რაც არ არის, შეამოწმოს, თუ არა გამოხატვის რეალურად ყალბი:

             if(!(expression)) { \

თუ ეს არის, იგი იყენებს NSLog შეხვიდეთ დეტალები უკმარისობა:

NSLog(@"Assertion failure: %s in %s on line %s:%d. %@", #expression, __func__, __FILE__, __LINE__, [NSString stringWithFormat: @"" __VA_ARGS__]); \

#expression მშენებლობა აწარმოებს string ლიტერატურული შემცველი ტექსტის გამოთქმა. მაგალითად, ეს იქნება აწარმოოს "x > 0" პირველი ამტკიცებენ, დარეკეთ ზემოთ. საქართველოს__func__ იდენტიფიკატორი აწარმოებს სახელი მიმდინარე ფუნქცია. __FILE__ და __LINE__ უნდა იყოს თვითმმართველობის განმარტებითი. რაღაც @"" ამ stringWithFormat: დარეკეთ უზრუნველყოფს, რომ სინტაქსი არის იურიდიული მაშინაც კი, როდესაც არ არის მიზეზი, string არის გათვალისწინებული.

მას შემდეგ, რაც ხე მტკიცება, მარცხი, ეს მაშინ წყვეტს app დარეკვით abort, და მაკრო მთავრდება:

abort(); \
            } \
        } while(0)

ამ მუშაობს იდეალურად. ეს საშუალებას აძლევს დამატებითი განმარტებითი ტექსტი, მაგრამ არ საჭიროებს ის შემთხვევები, სადაც გამოხატვის თავისუფლება არ არის საკმარისი, რათა ეს ნათელი, რა ხდება, არასწორია. ეს ყოველთვის მოუწოდებს abort უკმარისობა, ვიდრე სროლა გამონაკლისი, რომელიც შესაძლოა იყოს დაჭერილი. ეს ჟურნალი ყველა არსებული დეტალები წერტილი უკმარისობა.

განცხადება კონკრეტული ინფორმაცია,
ეს იქნება დიდი თუ ჩვენ ვერ მარცხი გაგზავნა გამოჩნდება crash ჟურნალი, ასევე. თურმე, ჩვენ შეგვიძლია! Wil Shipley აჩვენა , თუ როგორ უნდა დააყენოს საბაჟო მონაცემთა შევიდა “პროგრამა კონკრეტული ინფორმაცია,” მონაკვეთზე ავარიის შესვლა. დააყენა ეს სადღაც წყარო კოდი:

const char *__crashreporter_info__ = NULL;
    asm(".desc ___crashreporter_info__, 0x10");

ნებისმიერი string დაწერილი ამ ჯადოსნური გლობალური ცვლადი გამოჩნდება, რომ მონაკვეთზე ავარიის შესვლა. ეს არ მუშაობს ყველგან (სიტყვა ის არის, რომ ეს არ იმუშავებს iOS), მაგრამ ეს შეიძლება იყოს მოსახერხებელი, და არ მიაყენოს, როდესაც იგი არ მუშაობს. თუ გსურთ რომ ისარგებლოთ ამ, მცირე მოდიფიკაცია ამტკიცებენ, მაკრო დააყენა შეტყობინება შევიდა ეს ცვლადი, ისევე, როგორც ხე იგი:

#define MAAssert(expression, ...) \
        do { \
            if(!(expression)) { \
                NSString *__MAAssert_temp_string = [NSString stringWithFormat: @"Assertion failure: %s in %s on line %s:%d. %@", #expression, __func__, __FILE__, __LINE__, [NSString stringWithFormat: @"" __VA_ARGS__]]; \
                NSLog(@"%@", __MAAssert_temp_string); \
                __crashreporter_info__ = [__MAAssert_temp_string UTF8String]; \
                abort(); \
            } \
        } while(0)

და, თითქოს მიერ ჯადოსნური, გაგზავნა, როგორც ჩანს, ავარიის შესვლა.

ფილოსოფია
არის, რომ თქვენ იცით, თუ როგორ უნდა დაწეროს ამტკიცებენ, ბევრი სხვადასხვა გზა, უბრალოდ , თუ რა სახის და ამტკიცებს, უნდა წერთ?

ამტკიცებს, უნდა იყოს დაწერილი პირობები, რომ, თანახმად თქვენი გაგება პროგრამა, უნდაარასოდეს მოხდეს. ამტკიცებს, უნდა არ იყოს გამოყენებული, რათა შეამოწმოს შეცდომები, რომლებიც რეალურად მოსალოდნელია, რომ მოხდება რიგ შემთხვევებში. მაგალითად, დამადასტურებელი, რომ filename არის, არ nil არის კარგი ტექნიკით:

assert(filename != nil);

თუმცა, დამადასტურებელი, რომ მონაცემები შეიძლება წაკითხული რომ ფაილი არის ცუდი პრაქტიკა:

NSData *data = [NSData dataWithContentsOfFile: filename];
    assert(data != nil);

რომ ზარი შეიძლება ლეგიტიმურად ვერ, იმის გამო, რომ რეალური პირობები, როგორიცაა ფაილი, არ არსებული დისკზე, ან არ გააჩნიათ უფლებები, რომ წაიკითხავს. იმის გამო, რომ, ამ კოდექსის სჭირდება გარკვეული ფაქტობრივი შეცდომა გატარება, არა მხოლოდ ამტკიცებენ. ვერ წაიკითხა ფაილი უნდა გამოიწვიოს აღების ალტერნატიული მიდგომა ან საგანგაშო შესახებ, რომ რაღაც წავიდა არასწორი, არა მხოლოდ ხე და შეწყვეტის ოთახი.

როგორც წესი, ყველაზე სასარგებლო ადგილი ამტკიცებს, ზედა ფუნქცია ან მეთოდი, რათა შეამოწმოს შეზღუდვების პარამეტრების, რომელიც არ შეიძლება იყოს გამოხატული ენა პირდაპირ. ეს ამტკიცებს, შეესაბამება პირდაპირ შეზღუდვების გამოთქვა დოკუმენტაცია. მაგალითად:

// Flange an array of sprockets. The sprockets array must contain
    // at least two entries, and the index must lie within the array.
    - (void)flangeSprockets: (NSArray *)array fromIndex: (NSUInteger)index
    {
        assert([array count] >= 2);
        assert(index < [array count]);

        ...method body...

უფსკრული აბონენტის და callee ხდის ადვილი დასაკარგი სიმღერა, ეს შეზღუდვები, რაც ამ შესანიშნავი ადგილი, რათა გადაამოწმოს, რომ ყველაფერი ისე, როგორც ეს უნდა იყოს. განსაკუთრებული ყურადღება უნდა მიექცეს პარამეტრების, რომელიც ადვილად screw up, და პარამეტრების, სადაც ცუდი ღირებულებების გამოიწვევს უცნაური წარუმატებლობის. მაგალითად, ამ ამტკიცებენ, შემოწმების NULL, მიუხედავად იმისა, რომ ჯერ კიდევ სასარგებლო, არ დაამატოთ ბევრი, მას შემდეგ, რაც შედეგად ავარიის გარეშე ეს მაინც იყოს საკმაოდ ნათელია:

assert(ptr != NULL);
    x = *ptr;

ეს არ არის ცუდი, მაგრამ თქვენი დრო შეიძლება იყოს უკეთესი, გაატარა მის ფარგლებს გარეთ. ეს ამტკიცებენ, შემოწმების nil არის ძალიან მოსახერხებელი, როგორც nil არც აქ იქნება პირველი შედეგი უცნაურად აშენდა string, რომელიც შეიძლება ნახოთ შორს და გაცილებით გვიან:

assert(name != nil);
    str = [NSString stringWithFormat@"Hello, %@!", name];

ეს შეიძლება ასევე იყოს მოსახერხებელი დაამატოთ ამტკიცებს, შუა კომპლექსი კოდი, რომელსაც აქვს ნათელი წინასწარ ან პოსტ პირობები. მაგალითად, შუა შეცვლის მონაცემთა სტრუქტურა, თქვენ შეიძლება შემოწმება, რათა დავრწმუნდეთ, რომ ყველა თქვენი ცვლადები, აქვს თანმიმდევრული ღირებულებებს შორის თავს:

assert(done + remaining == total);

ეს ნება თქვენ დაჭერა ლოგიკური შეცდომები სწრაფად.

თავიდან ამტკიცებს, გასაგები პირობები რომ აქვს პატარა ოთახი შეცდომა. მაგალითად, ეს არის უაზრო:

int x = 1;
    assert(x == 1);

    for(int i = 0; i < 10; i++)
    {
        assert(i >= 0);
        ...

არ არსებობს გზა, ეს ამტკიცებს, იქნება ცეცხლი, თუ კომპიუტერი არის სერიოზულად გამოსული, ასე რომ, ისინი ძირითადად ნარჩენები დრო. კონცენტრირება რამ, რომ “არ შეიძლება მოხდეს”, თუ ნაწილები თქვენი პროგრამა მუშაობს ერთად, რაც უნდა, მაგრამ, რომ თქვენ ვერ ძალუძს მენატრება.

და ბოლოს, დარწმუნდით, რომ პირობები, თქვენ დამადასტურებელი მიმართაც არსებობს საფუძვლიანი ვარაუდი, სწრაფად შევაფასოთ. თქვენ არ მინდა, მათ bogging ქვემოთ თქვენი პროგრამა. არ loop მეშვეობით თქვენი მლნ-ელემენტს array დამადასტურებელი რთული მდგომარეობა ყოველ ჩანაწერში მხოლოდ გარეთ პარანოია.

მოკლედ, ამტკიცებენ, აუცილებელი წინაპირობები თქვენი კოდი, თვალის მიმართ რამ, რაც გამოიწვევს თქვენ ტკივილი თუ არ დაიჭირეს დასაწყისში. მიზანი არის ის, რომ ფეხი გამართვის, როდესაც ყველაფერი დაიწყება არასწორია.

გამორთვა ამტკიცებს,
თუ ძიება ვებ ინფორმაცია იმის შესახებ, ამტკიცებს, თქვენ უცვლელად ოჯახიდან დისკუსიები, თუ როგორ უნდა გამორთოთ ამტკიცებს, თქვენს გამოშვების აშენებს. ყველაზე ამტკიცებენ სისტემები აქვს გზა უნდა გამორთოთ ამტკიცებს, პროგრამა მასშტაბით. C assert ზარი, შექმნის NDEBUG მაკრო გამორთავს მას. იყიდება კაკაოს ამტკიცებენ, მოუწოდებს, შექმნის NS_BLOCK_ASSERTIONS მაკრო ხდის მათ. არსებობს ზოგადად ორი მიზეზის გამო მოცემულია გამორთვა ამტკიცებს გამოშვების აშენებს:

  1. ამტკიცებს, დააწესოს runtime ღირებულება, რომ თქვენ არ უნდა მიიღოს, ყოველ მომხმარებელს გადაიხადოს. თეორიულად, თუ თქვენ გამოცდილი საფუძვლიანად, თქვენ არ ექმნებათ არანაირი მტკიცება, წარუმატებლობის თქვენს გამოშვების აშენებს მაინც.
  2. არის მტკიცება მარცხი დაუყოვნებლივ წყვეტს აპლიკაცია, რომელიც მომხმარებლებს არ მომწონს. მოხსნის ამტკიცებს, თქვენ პროგრამა შანსი, გააგრძელოს ფუნქციონირება სახე შეცდომა.

მაგრამ, მე მტკიცედ აზრით, რომ გააუქმოთ ამტკიცებს გამოშვების აშენებს არის საშინელი იდეა. The runtime ღირებულება უნდა იყოს უმნიშვნელო, და თუ ეს არა, მაშინ თქვენ უნდა redo თქვენი ამტკიცებს, დაფიქსირება, რომ. რაც შეეხება თავიდან app შეწყვეტა, ამტკიცებს, უნდა იყოს დაწერილი, რომ ასეთი მარცხი ყოველთვის იმას ნიშნავს, რომ რაღაც წავიდა საშინლად არასწორია. ეს არის გამორიცხული, რომ app გააგრძელებს ფუნქციონირებას წინაშე, რომ. ეს უფრო სავარაუდოა, რომ ეს თქვენ crash. ასევე შესაძლებელია, რომ ეს გავაგრძელებთ გაშვებული, მაგრამ კორუმპირებული თქვენი მომხმარებლის მონაცემები. სუფთა ავარიის არის ბევრად უფრო სასურველია. არ კოდი არის უფასო შეცდომები, და crashing ადრეული და ცხადია, როდესაც a bug არის შეექმნა არის ბევრად უკეთესი, თუნდაც რელიზი აშენების გაშვებული მომხმარებლის მანქანა. მომტანი სუფთა crash შესვლა დაგეხმარებათ გამართვის წარუმატებლობის უფრო სწრაფად.

მაგალითად, MAAssert მაკრო ზემოთ არ გაქვთ რაიმე ჩაშენებული გზა უნდა გამორთოთ იგი ამ მიზეზის გამო. თუ თქვენ იყენებთ სხვადასხვა ამტკიცებენ, დაწესებულებაში, მე გირჩევთ, რომ თქვენ თავიდან ოდესმე გარდამტეხი მათ off.

დასკვნა
ამტკიცებს, არის ღირებული ინსტრუმენტი მწარმოებელ უკეთესი კოდი და მიღების პროცესში შეცდომები უფრო ადვილია, იპოვოს და დაფიქსირება. ამტკიცებს, უნდა იყოს გამოყენებული, არსად არ არსებობს შეზღუდვა, მნიშვნელობა რომ არ აღასრულებენ ენაზე. ჩემი ზოგადი სახელმძღვანელო არის, რომ თუ დოკუმენტი შეზღუდვა შეეხება სტუმრებს, თქვენ ასევე უნდა ითქვას, რომ ეს კოდი. თუ თქვენ ოდესმე თავს წერა გარკვეული კოდი, რომელიც იღებს თქვენ ფიქრი ბევრი, და აქვს ცვლადები, რომელთა ღირებულებები უნდა ეხებოდეს ერთმანეთს გარკვეული გზა არ აღასრულებენ ენა, ამტკიცებენ, რომ კოდი.

რომ ის დღეს. პარასკევი Q&A ამოძრავებს მკითხველს წინადადებები, როგორც ყოველთვის, ასე რომ, თუ არსებობს თემა, რომელიც გსურთ იხილოთ დაფარული აქ, გთხოვთ, გამოგვიგზავნოთ. დავბრუნდებით მალე კიდევ ერთი საინტერესო სტატია.