brew install peripheryapp/periphery/periphery
mint install peripheryapp/periphery
scan
أمر المسح هو الوظيفة الأساسية لـ Periphery. لبدء الإعداد الموجه، ما عليك سوى التغيير إلى دليل مشروعك وتشغيل:
periphery scan --setup
يعمل الإعداد الموجه فقط مع مشاريع Xcode وSwiftPM، لاستخدام Periphery مع أنظمة إنشاء غير تابعة لـ Apple مثل Bazel، راجع Build Systems.
بعد الإجابة على بعض الأسئلة، سيقوم Periphery بطباعة أمر الفحص الكامل وتنفيذه.
الإعداد الموجه مخصص للأغراض التمهيدية فقط، وبمجرد أن تتعرف على الأجهزة الطرفية، يمكنك تجربة بعض الخيارات الأكثر تقدمًا، والتي يمكن رؤيتها جميعًا من خلال periphery help scan
.
للحصول على نتائج متماسكة من Periphery، من الضروري فهم الآثار المترتبة على أهداف البناء التي اخترت تحليلها. على سبيل المثال، تخيل مشروعًا يتكون من ثلاثة أهداف: التطبيق والمكتبة والاختبارات. يستورد هدف التطبيق Lib، بينما تستورد أهداف الاختبارات كلاً من App وLib. إذا قمت بتوفير الثلاثة جميعًا إلى خيار --targets
، فستتمكن Periphery من تحليل مشروعك ككل. ومع ذلك، إذا اخترت تحليل التطبيق والمكتبة فقط، وليس الاختبارات، فقد يقوم Periphery بالإبلاغ عن بعض مثيلات التعليمات البرمجية غير المستخدمة والتي يتم الرجوع إليها فقط بواسطة الاختبارات. ولذلك، عندما تشك في أن Periphery قد قدم نتيجة غير صحيحة، فمن المهم أن تأخذ في الاعتبار الأهداف التي اخترت تحليلها.
إذا كان مشروعك يتكون من واحد أو أكثر من أطر العمل المستقلة التي لا تحتوي أيضًا على نوع من التطبيقات التي تستهلك واجهاتها، فستحتاج إلى إخبار Periphery بافتراض أن جميع الإعلانات العامة تُستخدم في الواقع من خلال تضمين --retain-public
خيار.
بالنسبة للمشاريع المختلطة بين Objective-C وSwift، ننصحك بشدة أن تقرأ عن الآثار المترتبة على ذلك على نتائجك.
بمجرد أن تستقر على الخيارات المناسبة لمشروعك، قد ترغب في الاحتفاظ بها في ملف تكوين YAML. إن أبسط طريقة لتحقيق ذلك هي تشغيل Periphery باستخدام خيار --verbose
. بالقرب من بداية الإخراج، ستشاهد القسم [configuration:begin]
مع التكوين الخاص بك بتنسيق YAML أدناه. انسخ التكوين والصقه في .periphery.yml
في جذر مجلد المشروع الخاص بك. يمكنك الآن ببساطة تشغيل periphery scan
وسيتم استخدام تكوين YAML.
يقوم Periphery أولاً ببناء مشروعك. بالنسبة لمشاريع Xcode، تم إنشاء المخططات المقدمة عبر خيار --schemes
باستخدام xcodebuild
. بالنسبة لمشاريع Swift Package Manager، يتم إنشاء الأهداف الفردية المقدمة عبر خيار --targets
باستخدام swift build
. يستخدم مترجم Swift تقنية تسمى الفهرس أثناء البناء لملء مخزن الفهرس الذي يحتوي على معلومات حول بنية الكود المصدري لمشروعك.
بعد إنشاء مشروعك، يقوم Periphery بتنفيذ مرحلة الفهرسة. بالنسبة لكل ملف مصدر عضو في الأهداف المقدمة عبر خيار --targets
، يحصل Periphery على معلوماته الهيكلية من مخزن الفهرس ويبني تمثيل الرسم البياني الداخلي الخاص به لمشروعك. يقوم Periphery أيضًا بتحليل شجرة بناء الجملة المجردة (AST) لكل ملف لملء بعض التفاصيل التي لا يوفرها مخزن الفهرس.
بمجرد اكتمال الفهرسة، يقوم Periphery بتحليل الرسم البياني لتحديد التعليمات البرمجية غير المستخدمة. تتكون هذه المرحلة من عدد من الخطوات التي تعمل على تغيير الرسم البياني لتسهيل تحديد سيناريوهات محددة للتعليمات البرمجية غير المستخدمة. تقوم الخطوة الأخيرة بتوجيه الرسم البياني من جذوره لتحديد الإعلانات التي لم تعد مرجعية.
الهدف من Periphery هو الإبلاغ عن حالات الإعلانات غير المستخدمة. الإعلان عبارة عن class
، struct
، protocol
، function
، property
، أو constructor
، enum
، typealias
، أو associatedtype
، وما إلى ذلك. وكما تتوقع، فإن Periphery قادر على تحديد الإعلانات البسيطة غير المرجعية، على سبيل المثال، class
لم تعد مستخدمة في أي مكان في موقعك. قاعدة التعليمات البرمجية.
يمكن للأجهزة الطرفية أيضًا تحديد المثيلات الأكثر تقدمًا للتعليمات البرمجية غير المستخدمة. ويشرح القسم التالي هذه الأمور بالتفصيل.
يمكن للمحيط تحديد معلمات الوظيفة غير المستخدمة. يمكن أيضًا تحديد مثيلات المعلمات غير المستخدمة في البروتوكولات والإعلانات المطابقة لها، بالإضافة إلى المعلمات في الطرق التي تم تجاوزها. يتم شرح كلا السيناريوهين أدناه.
سيتم الإبلاغ عن المعلمة غير المستخدمة لوظيفة البروتوكول على أنها غير مستخدمة فقط إذا كانت المعلمة غير مستخدمة أيضًا في جميع عمليات التنفيذ.
protocol Greeter {
func greet ( name : String )
func farewell ( name : String ) // 'name' is unused
}
class InformalGreeter : Greeter {
func greet ( name : String ) {
print ( " Sup " + name + " . " )
}
func farewell ( name : String ) { // 'name' is unused
print ( " Cya. " )
}
}
نصيحة
يمكنك تجاهل جميع المعلمات غير المستخدمة من البروتوكولات والوظائف المطابقة باستخدام خيار
--retain-unused-protocol-func-params
.
كما هو الحال مع البروتوكولات، يتم الإبلاغ عن معلمات الوظائف المتجاوزة على أنها غير مستخدمة فقط إذا كانت غير مستخدمة أيضًا في الوظيفة الأساسية وجميع الوظائف المتجاوزة.
class BaseGreeter {
func greet ( name : String ) {
print ( " Hello. " )
}
func farewell ( name : String ) { // 'name' is unused
print ( " Goodbye. " )
}
}
class InformalGreeter : BaseGreeter {
override func greet ( name : String ) {
print ( " Sup " + name + " . " )
}
override func farewell ( name : String ) { // 'name' is unused
print ( " Cya. " )
}
}
يتم دائمًا تجاهل المعلمات غير المستخدمة للبروتوكولات أو الفئات المحددة في الوحدات النمطية الأجنبية (مثل المؤسسة)، نظرًا لأنه ليس لديك حق الوصول لتعديل إعلان الوظيفة الأساسية.
يتم أيضًا تجاهل المعلمات غير المستخدمة للوظائف التي تستدعي fatalError
. غالبًا ما تكون هذه الوظائف مُهيئات مطلوبة غير مُنفذة في الفئات الفرعية.
class Base {
let param : String
required init ( param : String ) {
self . param = param
}
}
class Subclass : Base {
init ( custom : String ) {
super . init ( param : custom )
}
required init ( param : String ) {
fatalError ( " init(param:) has not been implemented " )
}
}
لا يتم استخدام البروتوكول الذي يتوافق مع كائن ما حقًا ما لم يتم استخدامه أيضًا كنوع وجودي، أو لتخصيص طريقة/فئة عامة. المحيط قادر على تحديد هذه البروتوكولات الزائدة عن الحاجة سواء كانت متوافقة مع كائن واحد أو حتى كائنات متعددة.
protocol MyProtocol { // 'MyProtocol' is redundant
func someMethod ( )
}
class MyClass1 : MyProtocol { // 'MyProtocol' conformance is redundant
func someMethod ( ) {
print ( " Hello from MyClass1! " )
}
}
class MyClass2 : MyProtocol { // 'MyProtocol' conformance is redundant
func someMethod ( ) {
print ( " Hello from MyClass2! " )
}
}
let myClass1 = MyClass1 ( )
myClass1 . someMethod ( )
let myClass2 = MyClass2 ( )
myClass2 . someMethod ( )
هنا يمكننا أن نرى أنه على الرغم من استدعاء كلا التطبيقين لـ someMethod
، إلا أن الكائن لا يتخذ في أي وقت نوع MyProtocol
. ولذلك فإن البروتوكول نفسه زائد عن الحاجة، وليس هناك فائدة من توافق MyClass1
أو MyClass2
معه. يمكننا إزالة MyProtocol
مع كل مطابقة زائدة عن الحاجة، والاحتفاظ someMethod
في كل فئة.
تمامًا مثل الطريقة العادية أو خاصية الكائن، يمكن أيضًا تحديد الخصائص والأساليب الفردية المعلنة بواسطة البروتوكول الخاص بك على أنها غير مستخدمة.
protocol MyProtocol {
var usedProperty : String { get }
var unusedProperty : String { get } // 'unusedProperty' is unused
}
class MyConformingClass : MyProtocol {
var usedProperty : String = " used "
var unusedProperty : String = " unused " // 'unusedProperty' is unused
}
class MyClass {
let conformingClass : MyProtocol
init ( ) {
conformingClass = MyConformingClass ( )
}
func perform ( ) {
print ( conformingClass . usedProperty )
}
}
let myClass = MyClass ( )
myClass . perform ( )
هنا يمكننا أن نرى أن MyProtocol
نفسه مستخدم ولا يمكن إزالته. ومع ذلك، بما أن unusedProperty
لا يتم استدعاؤه مطلقًا على MyConformingClass
، فإن Periphery قادر على تحديد أن إعلان unusedProperty
في MyProtocol
غير مستخدم أيضًا ويمكن إزالته مع التنفيذ غير المستخدم لـ unusedProperty
.
إلى جانب القدرة على تحديد التعدادات غير المستخدمة، يمكن لـ Periphery أيضًا تحديد حالات التعداد الفردية غير المستخدمة. يمكن تعريف التعدادات البسيطة التي لا يمكن تمثيلها بشكل خام، أي التي لا تحتوي على نوع قيمة String
أو Character
أو Int
أو الفاصلة العائمة، بشكل موثوق. ومع ذلك، فإن التعدادات التي لها نوع قيمة أولية يمكن أن تكون ديناميكية بطبيعتها، وبالتالي يجب افتراض استخدامها.
دعونا نوضح هذا بمثال سريع:
enum MyEnum : String {
case myCase
}
func someFunction ( value : String ) {
if let myEnum = MyEnum ( rawValue : value ) {
somethingImportant ( myEnum )
}
}
لا توجد إشارة مباشرة إلى حالة myCase
، لذا فمن المعقول أن نتوقع أنه ربما لم تعد هناك حاجة إليها، ولكن إذا تمت إزالتها، فيمكننا أن نرى أن somethingImportant
لن يتم استدعاؤه أبدًا إذا تم تمرير قيمة "myCase"
إلى someFunction
.
يتم تحديد الخصائص التي تم تعيينها ولكن لم يتم استخدامها مطلقًا على هذا النحو، على سبيل المثال:
class MyClass {
var assignOnlyProperty : String // 'assignOnlyProperty' is assigned, but never used
init ( value : String ) {
self . assignOnlyProperty = value
}
}
في بعض الحالات، قد يكون هذا هو السلوك المقصود، وبالتالي لديك بعض الخيارات المتاحة لإسكات هذه النتائج:
--retain-assign-only-property-types
. يجب أن تتطابق الأنواع المحددة مع استخدامها تمامًا في إعلان الخاصية (بدون علامة استفهام اختيارية)، على سبيل المثال String
, [String]
, Set
. المحيط غير قادر على حل أنواع الخصائص المستنتجة، لذلك في بعض الحالات قد تحتاج إلى إضافة تعليقات توضيحية صريحة للنوع إلى خصائصك.--retain-assign-only-properties
. يتم تحديد الإعلانات التي تم وضع علامة عليها كـ public
ولكن لم يتم الرجوع إليها من خارج الوحدة الرئيسية الخاصة بها، على أنها ذات إمكانية وصول عامة زائدة عن الحاجة. في هذا السيناريو، يمكن إزالة التعليق التوضيحي public
من التصريح. إن إزالة إمكانية الوصول العامة الزائدة عن الحاجة لها عدة فوائد:
final
من خلال الاكتشاف التلقائي لجميع الإعلانات التي يحتمل أن تكون متجاوزة. يتم تحسين الفئات final
بشكل أفضل بواسطة المترجم. يمكن تعطيل هذا التحليل باستخدام --disable-redundant-public-analysis
.
يمكن للأجهزة الطرفية اكتشاف الواردات غير المستخدمة من الأهداف التي قامت بفحصها، أي تلك المحددة باستخدام وسيطة --targets
. لا يمكنه اكتشاف عمليات الاستيراد غير المستخدمة لأهداف أخرى لأن ملفات مصدر Swift غير متوفرة ولا يمكن ملاحظة استخدامات @_exported
. @_exported
يمثل مشكلة لأنه يغير الواجهة العامة للهدف بحيث لا يتم بالضرورة الإعلان عن الإعلانات المصدرة بواسطة الهدف بواسطة الهدف المستورد. على سبيل المثال، Dispatch
هدف Foundation
بتصدير الإرسال، من بين أهداف أخرى. إذا كان أي ملف مصدر معين يستورد Foundation
ويشير إلى DispatchQueue
ولكن لا يوجد أي تعريفات أخرى من Foundation
، فلا يمكن إزالة استيراد Foundation
لأنه سيؤدي أيضًا إلى جعل نوع DispatchQueue
غير متاح. ولتجنب النتائج الإيجابية الكاذبة، فإن Periphery يكتشف فقط الواردات غير المستخدمة من الأهداف التي قام بمسحها.
من المحتمل أن تنتج Periphery نتائج إيجابية خاطئة للأهداف ذات Swift وObjective-C المختلطة، حيث لا يمكن لـ Periphery فحص ملفات Objective-C. ولذلك يوصى بتعطيل اكتشاف الاستيراد غير المستخدم للمشروعات التي تحتوي على قدر كبير من لغة Objective-C، أو استبعاد أهداف اللغات المختلطة يدويًا من النتائج.
لا يمكن للأجهزة الطرفية تحليل كود Objective-C نظرًا لأنه قد تتم كتابة الأنواع ديناميكيًا.
افتراضيًا، لا يفترض Periphery أن الإعلانات التي يمكن الوصول إليها بواسطة وقت تشغيل Objective-C قيد الاستخدام. إذا كان مشروعك عبارة عن مزيج من Swift وObjective-C، فيمكنك تمكين هذا السلوك باستخدام خيار --retain-objc-accessible
. إعلانات Swift التي يمكن الوصول إليها من خلال وقت تشغيل Objective-C هي تلك التي تم شرحها بشكل صريح باستخدام @objc
أو @objcMembers
، والفئات التي ترث NSObject
إما بشكل مباشر أو غير مباشر عبر فئة أخرى.
وبدلاً من ذلك، يمكن استخدام --retain-objc-annotated
للاحتفاظ فقط بالإعلانات الموضحة بشكل صريح بـ @objc
أو @objcMembers
. لا يتم الاحتفاظ بالأنواع التي ترث NSObject
إلا إذا كانت تحتوي على التعليقات التوضيحية الصريحة. قد يكشف هذا الخيار عن المزيد من التعليمات البرمجية غير المستخدمة، ولكن مع التحذير من أن بعض النتائج قد تكون غير صحيحة إذا تم استخدام التصريح بالفعل في كود Objective-C. لحل هذه النتائج غير الصحيحة، يجب عليك إضافة تعليق توضيحي @objc
إلى الإقرار.
يقوم Swift بتجميع تعليمات برمجية إضافية للأنواع Codable
غير المرئية لـ Periphery، ويمكن أن يؤدي إلى نتائج إيجابية خاطئة للخصائص التي لم يتم الرجوع إليها مباشرة من التعليمات البرمجية غير المركبة. إذا كان مشروعك يحتوي على العديد من هذه الأنواع، فيمكنك الاحتفاظ بجميع الخصائص في الأنواع Codable
باستخدام --retain-codable-properties
. وبدلاً من ذلك، يمكنك الاحتفاظ بالخصائص فقط على الأنواع Encodable
باستخدام --retain-encodable-properties
.
إذا تم الإعلان عن المطابقة Codable
بواسطة بروتوكول في وحدة نمطية خارجية لم يتم فحصها بواسطة Periphery، فيمكنك توجيه Periphery لتحديد البروتوكولات على أنها Codable
باستخدام --external-codable-protocols "ExternalProtocol"
.
يتم الاحتفاظ تلقائيًا بأي فئة ترث XCTestCase
مع طرق الاختبار الخاصة بها. ومع ذلك، عندما يرث الفصل XCTestCase
بشكل غير مباشر عبر فئة أخرى، على سبيل المثال UnitTestCase
، وتوجد هذه الفئة في هدف لم يتم فحصه بواسطة Periphery، فإنك تحتاج إلى استخدام خيار --external-test-case-classes UnitTestCase
لتوجيه Periphery إلى تعامل مع UnitTestCase
كفئة فرعية XCTestCase
.
إذا كان مشروعك يحتوي على ملفات Interface Builder (مثل القصص المصورة وXIBs)، فسيأخذ Periphery هذه الملفات في الاعتبار عند تحديد الإعلانات غير المستخدمة. ومع ذلك، يقوم Periphery حاليًا بتعريف الفئات غير المستخدمة فقط. يوجد هذا القيد لأن Periphery لم يقوم بعد بتحليل ملفات Interface Builder بشكل كامل (راجع الإصدار رقم 212). نظرًا لمبدأ تصميم Periphery المتمثل في تجنب الإيجابيات الكاذبة، فمن المفترض أنه إذا تمت الإشارة إلى فئة ما في ملف Interface Builder، فسيتم استخدام جميع IBOutlets
و IBActions
الخاصة بها، حتى لو لم تكن في الواقع. ستتم مراجعة هذا الأسلوب لتحديد IBActions
و IBOutlets
غير المستخدمة بدقة بمجرد اكتساب Periphery القدرة على تحليل ملفات Interface Builder.
لأي سبب من الأسباب، قد ترغب في الاحتفاظ ببعض التعليمات البرمجية غير المستخدمة. يمكن استخدام أوامر تعليق التعليمات البرمجية المصدر لتجاهل إعلانات محددة واستبعادها من النتائج.
يمكن وضع أمر تجاهل التعليق مباشرة على السطر أعلى أي إعلان لتجاهله، وجميع الإعلانات التابعة له:
// periphery:ignore
class MyClass { }
يمكنك أيضًا تجاهل معلمات دالة محددة غير مستخدمة:
// periphery:ignore:parameters unusedOne,unusedTwo
func someFunc ( used : String , unusedOne : String , unusedTwo : String ) {
print ( used )
}
يمكن وضع الأمر // periphery:ignore:all
في أعلى الملف المصدر لتجاهل محتويات الملف بالكامل. لاحظ أنه يجب وضع التعليق فوق أي رمز، بما في ذلك بيانات الاستيراد.
تدعم أوامر التعليق أيضًا التعليقات اللاحقة التي تتبع واصلة بحيث يمكنك تضمين شرح على نفس السطر:
// periphery:ignore - explanation of why this is necessary
class MyClass { }
قبل إعداد تكامل Xcode، نوصي بشدة أن تقوم أولاً بتشغيل Periphery في محطة طرفية، حيث ستستخدم نفس الأمر بالضبط عبر Xcode.
حدد مشروعك في Project Navigator وانقر فوق الزر + الموجود أسفل يسار قسم الأهداف. حدد النظام الأساسي المشترك واختر تجميع . ضرب التالي.
المحيط هو مشروع شغوف يتطلب قدرًا كبيرًا من الجهد للمحافظة عليه وتطويره. إذا وجدت Periphery مفيدًا، فيرجى التفكير في الرعاية من خلال رعاة GitHub.
شكر خاص للرعاة السخيين التاليين:
تقوم شركة SaGa Corp بتطوير تكنولوجيا فريدة للاعبين الماليين وعملائهم.
Emerge Tools عبارة عن مجموعة من المنتجات الثورية المصممة لتعزيز تطبيقات الأجهزة المحمولة والفرق التي تقوم بإنشائها.