تصحيح متقدم

Debug Area هي ميزة مفيدة في عمل مطوري iOS في Xcode. بمجرد البدء في إتقان تطوير نظام التشغيل iOS ، ومحاولة الابتعاد عن طريقة الطباعة المألوفة والمحبوبة ، والعثور على طرق أسرع وأكثر ملاءمة لفهم حالة النظام في فترة معينة ، بدأنا في دراسة Debug Area.

على الأرجح ، في لوحة Debug ستسقط عينيك قبل أن تفهم ما يحدث بالضبط هناك. عندما يتعطل التطبيق لأول مرة ، يتم فتح القائمة السفلية تلقائيًا ، ويمكن أن يساعد في البداية على فهم المشكلة (تذكر "الخطأ الفادح: القديم الجيد: الفهرس خارج النطاق") ، في البداية لن تفهم ما يريده Xcode منا و google it الأخطاء ، ولكن في سياق النمو والمزيد والمزيد من المعلومات سوف تصبح واضحة.

منذ البداية ، يحاول المبرمج تحسين عمله. للقيام بذلك ، نحن نسعى جاهدين لفهم في أي نقطة ذهب برنامجنا إلى حالة غير صحيحة. وهنا ، بناءً على النقطة التي يوجد بها تطور المبرمج ، قد تختلف الأساليب. أولاً ، كيفية إجراء Debug بطريقة صحيحة من خلال طريقة "print ()" ، ثم يتم وضع Breakpoints وتسمى أساليب "po" ، ثم التعرف على Debug Variable Input (المنطقة المجاورة لوحدة التحكم في Xcode) ، ثم يأتي فهم كيفية ترجمة الشفرة أثناء التوقف طرق توقف - "التعبير" (على الأقل كان هذا التطور الذي كان لدي).

دعونا نجرب طرقًا مختلفة ستساعدنا على فهم وتغيير حالة تطبيقنا. لن يتم النظر في أبسط مثل "print ()" و "po" ، وأعتقد أنك تفهم بالفعل جوهرها ومعرفة كيفية تطبيقها.

دعونا ننشئ تطبيقًا بسيطًا بشاشة واحدة سيكون لدينا فيها نوع واحد فقط من الخلايا (TableViewcell) مع عنصرين داخل: UIImageView و UILabel. في الخلايا ، سنكتب رقمه التسلسلي ، ونضع إما صورة 1 أو صورة 2 في الصورة.

ستعمل طريقة tableViewCellForRowAtIndexPath على إنشاء خلايا لنا ووضع البيانات وإرجاعها:

صورة

ستنشئ هذه الطريقة الجدول التالي:

صورة

توقف


دعونا نتوقف عن برنامجنا ونضيف بعض النص إلى الملصق الخاص بنا.

1. تعيين نقطة توقف:

صورة

2. توقف البرنامج عن التنفيذ على السطر 55 ، مباشرة بعد تعيين النص. نظرًا لأننا على خط يقع في مجال رؤية الخلية ، يمكننا التفاعل مع خليتنا.

3. نكتب في وحدة التحكم أمرًا لتغيير نص الخلية:

صورة

4. نقوم بإزالة برنامج Breakpoint الخاص بنا والضغط على الزر "مواصلة تنفيذ البرنامج".

5. على شاشة هاتفنا نرى أن كل شيء قد تحول بنجاح:

صورة

تعبير ينفذ التعبير ويعيد قيمة في سلسلة الرسائل الحالية.

تحرير نقطة توقف


ولكن ، ماذا لو كنا بحاجة إلى تغيير النص في عدد كبير من الخلايا؟ أم أننا أدركنا بالفعل في عملية تنفيذ البرنامج أننا بحاجة إلى التغيير؟

يمكننا تحسين تنفيذ هذه العملية وتسريع العمل بعض الشيء ، وإجراء تغييرات على النص عندما تصل إلى Breakpoint ومواصلة تنفيذ البرنامج ، وهذا سيوفر الكثير من الوقت ويسمح لنا بعدم طباعة نفس الشيء لكل خلية.

للقيام بذلك ، نحتاج إلى تعديل Breakpoint الخاص بنا قليلاً ، وإضافة رمز إضافي هناك ، والذي سيغير نصه في منطقة رؤية خليتنا ومواصلة البرنامج.

  1. إنشاء نقطة توقف.
  2. انقر بزر الفأرة الأيسر على سهم نقطة الإيقاف.
  3. انقر فوق تحرير نقطة الفصل.
  4. الشرط - الشروط التي تعمل بها Breakpoint ، والآن نحن لسنا بحاجة إليها.
  5. تجاهل - كم مرة لتخطي Breakpoint قبل أن يعمل (وليس ذلك أيضًا).
  6. لكن الإجراء هو ما نحتاج إليه ، حدد نوع الإجراء Debugger Command.
  7. نكتب التعبير الذي نحتاج إلى تنفيذه:
  8. تعبير cell.desriptionTextOutlet.text = "\ (indexPath.item) المهمة مكتملة".
  9. ضع علامة - متابعة التنفيذ بعد الانتهاء بنجاح من الأمر.

صورة

9. نحن نحاول.

صورة

لقد نجح هذا الأمر ، فقد تم تغيير النص لكل خلية أثناء تكوين الجدول ، ولم يكن علينا التضحية بالوقت ونصف العمليات لكل منها.

وظيفة نقطة توقف


هناك دائمًا أوقات يحدث فيها شيء ما في تطبيقنا لا يمكننا تفسيره ، ولا يتغير النص أو يتغير أكثر من اللازم ، ويبدو أن Breakpoint في هذه الحالة ليس لديه مكان. ولكن هذا ليس صحيحًا تمامًا ، إذا كنت تعرف Obj-C ، وكنت تعرف الطريقة التي ينفذ بها برنامج التحويل البرمجي الذي تريد تتبعه ، فيمكنك وضع Breakpoint عليه وفي المرة التالية التي يتم فيها استدعاء الطريقة ، يتوقف التطبيق في عملية تنفيذ التعليمات البرمجية Assembler.

1. في لوحة التجول Breakpoint ، حدد Symbolic Breakpoint.

صورة

2. نريد تتبع طريقة تعيين النص في الخلية ، والكتابة - [UILabel setText:].

صورة

3. وسيطة الصفر غير موجودة ، ويبدأ العد من الأول. الطريقة الأولى التي تم اكتشافها ليست هي ما نحتاج إليه (لقد قمنا بتعيين الوقت الحالي على شريط الحالة) ، ولكن الطريقة الثانية هي فقط:

4. تحت عنوان "arg1" ، يتم تخزين وصف الكائن.

5. تحت عنوان "arg2" ، يتم تخزين وظيفة المحدد.

6. تحت عنوان "arg3" ، يتم تخزين النص الذي تم الحصول عليه بواسطة الطريقة.

حسنا ، هذا يبدو واضحا. لكن في بعض الأحيان تكون هناك مواقف عندما لا يقتصر تثبيت نص واحد في شريط الحالة ، وتحتاج إلى تتبع تنفيذ الطريقة في وحدة تحكم معينة ، فما الذي يجب عمله؟ يمكنك تمكين Breakpoint على غرار ما قمنا بتثبيته مسبقًا ، ولكن يمكنك تحديد موضعه في الكود. ماذا يعني هذا؟ نحن على يقين من أن عرضنا سيظهر عندما نقوم بتثبيت النص في الخلية ، وبالتالي فإن الشيء نفسه هو وضعه في viewDidLoad أو بعد إنشاء الخلية.

لإنشاء نقطة توقف ، وضعناها على السطر ، وفي الإجراء نكتب الرمز التالي:

breakpoint set --one-shot true --name "-[UILabel setText:]” 

breakpoint set —one-shot true - إنشاء نقطة توقف
—name هو اسم نقطة توقف الأحرف
“-[UILabel setText:]” الطريقة

إليك ما حدث:

صورة

تخطي الخطوط



ولكن ماذا لو اشتبهنا في أن سطر التعليمات البرمجية يفسد البرنامج بأكمله؟ أثناء تنفيذ التعليمات البرمجية ، يمكنك تجنب تنفيذ سطر معين من التعليمات البرمجية مثل هذا:

  1. نضع نقطة توقف على خط لا نرغب في تنفيذه.
  2. عندما يتوقف التنفيذ ، اسحبه إلى السطر الذي نريد مواصلة تنفيذ البرنامج به (مضحك ، لكنه لا يعمل دائمًا ، يكون الخيار دون السحب أقل).

هناك أيضًا خيار آخر يعمل على تحسين تخطي الخط - هذا هو وصف الأمر المناسب في "نقطة توقف التعديل". الفريق محفوف بالمخاطر ، نظرًا لأن جوهر هذه القفزات هو إنقاذنا من عملية إعادة الإنشاء ، ولكن إذا تخطيت تهيئة الكائن وحاولت الاتصال به ، فسيتم تعطل البرنامج.

صورة

دعنا نوقف برنامجنا عن تهيئة الصورة ، ولن نخصص الصورة للخلية على الإطلاق ، لذلك نحن بحاجة إلى تخطي خمسة أسطر من التعليمات البرمجية وإرجاع الخلية بدون صورة ، ولهذا فإننا نتخطى تنفيذ الأسطر الخمسة التالية من التعليمات البرمجية على الخيط الحالي ، ومتابعة البرنامج:

صورة

يبدو جيدًا ، لكن ما زلت أريد تعيين صورة ، دعنا نضيف طريقة تعيين إلى نقطة توقف:

صورة

مزيج جيد ، والآن لدينا نوع واحد فقط من الصور في كل خلية.

خدمات WatchPoint


وظيفة أخرى مريحة في المصحح هي تتبع القيم في البرنامج ، نقاط المراقبة. يشبه Watchpoint إلى حد ما KVO ، فنحن نضع نقطة توقف لتغيير حالة الكائن ، وفي كل مرة يغير فيها حالته ، تتوقف عملية تنفيذ البرنامج ، ويمكننا أن نرى القيمة والأماكن التي تم من خلالها تغيير القيمة. على سبيل المثال ، وضعت نقطة مراقبة على خلية لمعرفة ما يحدث عندما يتم ترحيل جدول ويتم تهيئة خلية جديدة. تبين أن قائمة الأوامر كانت كبيرة جدًا ، لذا لن أعطيها فقط ذكر بعض منها: تنفيذ عرض التخطيط الموجود داخل الخلية ووضع قيود ، والرسوم المتحركة ، ووضع حالات للخلية وأكثر من ذلك بكثير.

لتعيين نقطة مراقبة على قيمة ، يجب إيقاف برنامج نقطة التوقف في نطاق الخصائص التي تريد مراقبتها ، وتحديد الخاصية في لوحة "debug variable" وتحديد watch "<parameter>".

صورة

من أجل إزالة نقطة مراقبة مع متغير ، تحتاج إلى النظر في متصفح نقطة التوقف ، وهناك مع بقية نقطة التوقف ستكون نقطة مراقبة لدينا.

نقطة توقف تغيير واجهة المستخدم


في بعض الأحيان نحتاج إلى معرفة المزيد عن الكائن الذي نحاول صده. الخيار الأسهل هو استخدام "po" لعرض معلومات حول الكائن ، وإلقاء نظرة على موقع الكائن في الذاكرة في نفس المكان. لكن يحدث أنه ليس لدينا رابط مباشر لكائن ما ؛ فهو غير ممثَّل في عرض واجهة برمجة التطبيقات ، حيث تقع المكتبة أو ربما تخفيه. أحد الخيارات هو استخدام "عرض التسلسل الهرمي" ، ولكن هذا ليس مناسبًا دائمًا وليس من الصعب دائمًا فهم أنك قد وجدت العرض المطلوب. يمكنك محاولة استخدام الأمر:

 expression self.view.recursiveDescription() 

إنه في Obj-C ، لكن تمت إزالته في Swift نظرًا لخصائص اللغة ، لا يمكننا القيام بذلك ، ولكن نظرًا لأن Debuger يعمل مع Obj-C ، فمن الناحية النظرية يمكنه إطعام هذا الأمر وسيفهم ما تريده منه . لتنفيذ شفرة Obj-C في وحدة التحكم ، يجب إدخال الأمر:

 expression -l objc -O - - [`self.view` recursiveDescription] 

ماذا ترى هنا؟ أرى إنشاءًا غير مريح إلى حد ما يمكن أن يعتاد عليه بمرور الوقت ، لكن من الأفضل ألا نفعل ذلك ، لكننا نستخدم typealias لتبسيط الأمر:

 command alias poc expression -l objc -O — 

الآن فريقنا يتقلص ويبسط ، لكنه يواصل القيام بالعمل:

 poc [`self.view` recursiveDescription] 

هل ستعمل بعد إغلاق Xcode أو في مشروع آخر؟ للأسف ، لا. ولكن يمكن أن تكون ثابتة! عن طريق إنشاء ملف. ldbinit وإدخال الاسم المستعار الخاص بنا هناك. إذا كنت لا تعرف كيف ، فإليك التعليمات الخاصة بالعناصر:

1. إنشاء ملف. ldbinit (يمكنك أن تأخذ. gitignore كنموذج أولي ، فهو ينتمي إلى نفس النوع من الملفات غير المرئية النصية).

2. اكتب بالضبط الأمر التالي في هذا الملف:

  command alias poc expression -l objc -O - - 

3. ضع الملف في المجلد حسب العنوان "MacintoshHD / Users / <إليك اسم المستخدم الخاص بك>".

وهكذا حصلنا على وصف لجميع المشاهدات المقدمة على الشاشة. دعونا نحاول أن نرى ما يمكن أن نفعله مع عنوان الكائنات في الذاكرة. بالنسبة لـ Swift ، هناك طريقة ذات عيب ، يجب عليك دائمًا إلقاء نوع الكائن في الذاكرة على قيمة معينة:

 po unsafeBitCast(0x105508410, to: UIImageView.self) 

الآن يمكننا أن نرى موضع صورتنا في الخلية ، دعنا ننقلها بحيث تتمركز على الخلية ولها مسافة بادئة على جانب 20 بكسل.

صورة

في بعض الأحيان لا يكون التغيير ملحوظًا على الفور ، ولكن من الضروري إزالة التطبيق من التصحيح لإشعار التغيير.

ولكن إذا كنا نريد أن نرى شيئًا مماثلاً في كل خلية ، فنحن بحاجة إلى تسريع تنفيذ الأوامر ، يمكننا كتابة العديد من البرامج النصية في بيثون التي ستعمل من أجلنا (يمكنك معرفة كيفية إضافة البرامج النصية هنا www.raywenderlich.com/612-custom-lldb-commands-in- الممارسة ) ، وإذا كنت تعرف كيفية التعامل مع بيثون وتريد الكتابة عليه ل lldb ثم سوف تأتي في متناول اليدين.

قررت أن أكتب امتدادًا لفئة UIView ، والتي ستنقل العرض ببساطة في الاتجاه الصحيح ، بدا لي أنه سيكون هناك مشاكل أقل في توصيل البرامج النصية الجديدة بـ LLDB ولن يكون من الصعب على أي مبرمج لنظام iOS (وإلا فإنك تحتاج إلى تعلم Python لـ LLDB).

لم أكن أبحث عن مكان الكائن في الذاكرة وإحضاره إلى الفئة المرغوبة ، حتى أأخذ إطارًا لاحقًا ، سيستغرق أيضًا وقتًا طويلاً. تم حل السؤال عن طريق كتابة وظيفة في ملحق UIView:

صورة

لسوء الحظ ، لا يعمل بشكل جيد مع الخلايا ، ويرجع ذلك على الأرجح إلى أنه في الوقت الذي تم فيه تنفيذ أمر التدفق ، لم يتم حساب كل مواضع الخلية ولم تظهر على الشاشة (لم نرجع tableViewCell حتى الآن). مع العناصر الساكنة الأخرى ، فإنه يعمل بشكل جيد.

من خلال معرفة موضع العرض في التسلسل الهرمي ، يمكننا الوصول إليه وتغيير موقعه.

والآن فإن الوضع العكسي هو عندما نتمكن من الوصول إلى ViewHierarchy ونريد الحصول على عرض البيانات من هناك. في Xcode ، من الممكن عرض التسلسل الهرمي للعرض أثناء تنفيذ البرنامج ، ويمكنك أيضًا عرض الألوان والتخطيط والأنواع والارتباطات بالكائنات الأخرى ، بما في ذلك ذلك. دعونا نحاول الوصول إلى قيود UIImageView لدينا.

للحصول على بيانات القيد:

1. انقر فوق التسلسل الهرمي لعرض Debug.
2. قم بتمكين المحتوى المقتطع في اللوحة في أسفل الشاشة التي تظهر.
3. تمكين القيود في نفس اللوحة.
4. حدد كونتراينت.
5. في القائمة ، انقر فوق تحرير -> نسخ (Command + C).
6. يتم نسخ الربط من هذا النوع: ((NSLayoutConstraint *) 0x2838a39d0).
7. والآن ، كما قمنا بتغييره من خلال الكود ، يمكنك أيضًا تغييره في lldb:
expression [((NSLayoutConstraint *)0x2838a39d0) setConstant: 60]
8. بعد النقر على زر المتابعة ، سيقوم العنصر بتحديث موقعه على الشاشة.

يمكنك تغيير الألوان والنصوص وغير ذلك بنفس الطريقة:

 expression [(UILabel *)0x102d0a260] setTextColor: UIColor.whiteColor] 

تبين أن مشروع العرض التوضيحي بسيط للغاية (60 سطرًا من التعليمات البرمجية في ViewController) ، يتم تقديم معظم التعليمات البرمجية التي كتبت في المقال ، لذلك لن تكون هناك صعوبة في إعادة إنتاج مشروع الاختبار.

ملاحظة: إذا كان لديك أسئلة أو تعليقات ، فاكتب. ألق نظرة على WWDC و Debate كـ Pro.

كما أنصحك أن تتعرف على المواد:

مستوحاة من دورة مصحح الأخطاء المتقدمة WWDC 18
أوامر مصحح الأخطاء
إضافة البرامج النصية Python إلى LLDB Xcode

Source: https://habr.com/ru/post/ar460779/


All Articles