Swift: الإدخال والإخراج والتصحيح في Swift

كتابة الكود نصف المهمة ��قط. يعلمك هذا الدرس كيفية استخدام print والتأكيدات ونقاط التوقف لإيجاد وإصلاح الأخطاء في كودك.

1. ما ستتعلمه


2. قصة حقيقية: مبتدئ تعلم ذاتيًا

(1) نقطة الألم: كود صحيح منطقيًا، نتائج خاطئة

Mike هو مبتدئ في Swift يتعلم ذاتيًا يعمل على برنامج لحساب متوسط الدرجات:

SWIFT
let scores = [85, 92, 78, 90, 88]
var total = 0
for i in 0...5 {
    total += scores[i]
}
let average = total / 5
print("Average: \(average)")

تعطل البرنامج فورًا. لم يستطع Mike معرفة السبب. قضى 30 دقيقة في البحث عبر الإنترنت، مجربًا حلولاً مختلفة. كل تعديل يتطلب إعادة التشغيل، لكنه ما زال لا يستطيع تحديد المشكلة.

(2) الحل: التصحيح باستخدام print

اقترح صديق استخدام print لإخراج المتغيرات الوسيطة لتحديد المشكلة:

SWIFT
let scores = [85, 92, 78, 90, 88]
print("Array length: \(scores.count)")     // Output: 5
var total = 0
for i in 0..<scores.count {               // Use count instead of hardcoding
    print("Processing index \(i): value = \(scores[i])")
    total += scores[i]
}
print("Total: \(total)")
let average = total / 5
print("Average: \(average)")

المخرج��ت:

TEXT 📖 للعرض فقط
Array length: 5
Processing index 0: value = 85
Processing index 1: value = 92
Processing index 2: value = 78
Processing index 3: value = 90
Processing index 4: value = 88
Total: 433
Average: 86

بإخراج المتغيرات الوسيطة، اكتشف Mike فورًا أن فهارس المصفوفة هي 0-4، لكنه استخدم 0...5 (الذي يتضمن الفهرس 5).

(3) النتيجة: كفاءة التصحيح بعد إتقان print

البُعد قبل (التخمين) بعد (تصحيح print)
وقت تحديد المشكلة 30+ دقيقة 1-2 دقيقة
دقة الإصلاح 50% (غالبًا خاطئ) 95%
فهم الكود "لا أعرف لماذا تعطل" "أعرف الحالة في كل خطوة"
الثقة في حل المشكلات الجديدة 3/10 8/10

3. استخدام print المتقدم

print هي أداة التصحيح الأكثر استخدامًا في Swift، لكن وسائطها تفعل أكثر من مجرد إخراج قيمة.

100%
graph TB
    A[دالة print] --> B["items: القيم المراد إخراجها"]
    A --> C["separator: المحدد"]
    A --> D["terminator: حرف الن��اية"]
    A --> E["to: هدف الإخراج"]
    B --> F[قيم متعددة مفصولة بفواصل]
    C --> G["الافتراضي: مسافة"]
    C --> H["مخصص: | أو ، إلخ"]
    D --> I["الافتراضي: سطر جديد"]
    D --> J["مخصص: سلسلة فارغة"]
الوسيط النوع الافتراضي ��لغرض
items Any... مطلوب المحتوى المراد إخراجه
separator String " " المحدد بين العناصر المتعددة
terminator String "\n" حرف السطر الجديد النهائي
to TextOutputStream nil هدف الإخراج (الافتراضي: وحدة التحكم)

(1) separator وterminator

SWIFT
// Default: space-separated, newline-terminated
print("Hello", "Swift", "World")
// Hello Swift World
// Custom separator
print("Hello", "Swift", "World", separator: ", ")
// Hello, Swift, World
// Custom terminator (no newline)
print("Loading", terminator: "...")
print("Done")
// Loading...Done
// Combined usage
print("A", "B", "C", separator: " | ", terminator: ".\n")
// A | B | C.

(2) debugPrint وdump

debugPrint يخرج معلومات التصحيح (مع علامات اقتباس ومعلومات النوع)؛ dump يخرج الهياكل التفصيلية:

SWIFT
let name = "Alice"
let numbers = [1, 2, 3]
print(name)          // Alice
debugPrint(name)     // "Alice"
dump(name)           // - "Alice"
print(numbers)       // [1, 2, 3]
debugPrint(numbers)  // [1, 2, 3]
dump(numbers)
// ▿ 3 elements
//   - 0 : 1
//   - 1 : 2
//   - 2 : 3
الدالة الغرض إخراج السلسلة إخراج المصفوفة
print إخراج عادي Alice [1, 2, 3]
debugPrint إخراج تصحيح (يظهر معلومات النوع) "Alice" [1, 2, 3]
dump إخراج هيكل تفصيلي - "Alice" عنصر واحد في كل سطر

▶ مثال: إخراج سجل منسق

SWIFT
// ============================================
// Simulate system log output
// Demonstrates advanced print parameters and debugPrint
// ============================================
let event = "USER_LOGIN"
let user = "Alice"
let statusCode = 200
let duration = 0.045
// 1. Use separator to format the log
print("[\(event)]", user, "Status: \(statusCode)", separator: " | ", terminator: "")
print(" (\(duration)s)")
// [USER_LOGIN] | Alice | Status: 200 (0.045s)
// 2. Output tabular data
print()
print("=== Report ===")
print("Item", "Price", "Qty", separator: " | ")
print("-----", "-----", "---", separator: " | ")
print("Book", "12.99", "3", separator: " | ")
print("Pen", "1.50", "10", separator: " | ")
print("Bag", "49.99", "1", separator: " | ")
// 3. debugPrint for development debugging
let input: String? = "test"
debugPrint("Debug: input = \(input)")
// "Debug: input = Optional(\"test\")"

المخرجات:

TEXT 📖 للعرض فقط
[تسجيل_دخول_مستخدم] | Alice | الحالة: 200 (0.045ث)

=== تقرير ===
العنصر | السعر | الكمية
----- | ----- | ---
كتاب | 12.99 | 3
قلم | 1.50 | 10
حقيبة | 49.99 | 1
تصحيح: input = Optional("اختبار")

4. التأكيدات والشروط المسبقة

التأكيدات والشروط المسبقة هي أدوات البرمجة الدفاعية المضمنة في Swift، تلتقط أخطاء المنطق مبكرًا أثناء التطوير.

100%
graph LR
    A[فحوص وق�� التشغيل] --> B[assert]
    A --> C[precondition]
    B --> D[وضع التصحيح فقط]
    B --> E[التقاط المشكلات أثناء التطوير]
    C --> F[التصحيح + الإصدار]
    C --> G[أخطاء غير قابلة للاسترداد]
الدالة نشطة في ا��غرض مثال
assert التصحيح فقط فحوص الاتساق الداخلي أثناء التطوير assert(age > 0)
assertionFailure التصحيح فقط تشغيل تأكيد غير مشروط assertionFailure("لا يجب الوصول هنا")
precondition جميع الأوضاع فحوص الشروط المسبقة precondition(!name.isEmpty)
preconditionFailure جميع الأوضاع إنهاء غير مشروط preconditionFailure("خطأ فادح")

(1) تأكيدات assert للتصحيح

تأخذ assert مفعولها فقط في وضع التصحيح؛ تُزال في وضع الإصدار، لذا لا يوجد تأثير على الأداء:

SWIFT
func calculateDiscount(price: Double, percent: Double) -> Double {
    assert(price > 0, "Price must be greater than 0")
    assert(percent >= 0 && percent <= 100, "Discount must be between 0-100")
    let discount = price * percent / 100.0
    return price - discount
}
let finalPrice = calculateDiscount(price: 100.0, percent: 20)
print(finalPrice)  // 80.0
// The following would trigger assertion failures in Debug mode:
// calculateDiscount(price: -10, percent: 20)  // ❌ assert fails
// calculateDiscount(price: 100, percent: 150) // ❌ assert fails

(2) فحوص precondition

تأخذ precondition مفعولها في وضعي التصحيح والإصدار — تنهي البرنامج فورًا عندما لا يمكن استيفاء الشرط:

SWIFT
func sendEmail(to address: String, message: String) {
    precondition(address.contains("@"), "Invalid email address: \(address)")
    precondition(!message.isEmpty, "Message cannot be empty")
    print("Sending email to \(address): \(message)")
}
sendEmail(to: "alice@example.com", message: "Hello!")
// Sending email to alice@example.com: Hello!
// The following would trigger precondition failures (all modes):
// sendEmail(to: "invalid", message: "Hi")
💡 نصيحة: assert هي "للفحوص الداخلية" (ضمان صحة كودك الخاص)؛ precondition هي "للعقود الخارجية" (ضمان استيفاء المستدعين للمتطلبات). استخدم assert بكثرة أثناء التصحيح؛ استخدم precondition لحدود API العامة.

▶ مثال: التحقق من صحة الوسائط

SWIFT
// ============================================
// User registration parameter checks
// Demonstrates assert and precondition usage
// ============================================
import Foundation
func registerUser(name: String, age: Int, email: String) {
    // precondition: Public API contract (active in all modes)
    precondition(name.count >= 2, "Username must be at least 2 characters")
    precondition(age >= 18, "User must be at least 18 years old")
    precondition(email.contains("@"), "Invalid email format")
    // assert: Internal logic check (Debug only)
    let emailParts = email.split(separator: "@")
    assert(emailParts.count == 2, "Email should contain exactly one @ symbol")
    let domain = String(emailParts[1])
    assert(domain.contains("."), "Email domain is invalid")
    // Actual registration logic
    print("Registration successful: \(name), age \(age)")
    print("Confirmation email sent to: \(email)")
}
// Valid call
registerUser(name: "Alice", age: 28, email: "alice@example.com")
print("---")
// Calls that would trigger precondition failures (commented out to avoid crashing)
// registerUser(name: "A", age: 20, email: "test@test.com")

المخرجات:

TEXT 📖 للعرض فقط
Registration successful: Alice, age 28
Confirmation email sent to: alice@example.com
---

5. تصحيح Playground

تقدم Playgrounds قدرات تصحيح أقوى من print، بما في ذلك المعاينات المباشرة ونقاط التوقف.

(1) المعاينة المباشرة في Playground

يعرض الشريط الجانبي في Playgrounds نتيجة كل سطر في الوقت الفعلي:

SWIFT
// Run in Playground — the sidebar shows each step's result
let name = "Alice"    // "Alice"
var score = 0         // 0
score += 85           // 85
score += 92           // 177
let average = score / 2  // 88
100%
graph TB
    A[تصحيح Playground] --> B[لوحة ال��تائج المباشرة]
    A --> C[تصحيح نقاط التوقف]
    A --> D[سجل القيم]
    B --> E[النتائج معروضة لكل س��ر]
    C --> F[إيقاف / خطوة / متابعة]
    D --> G[منحنيات تغير قيم المتغيرات]
الميزة كيفية الاستخدام الغرض
المعاينة المباشرة تحرير الكود يعرض النتائج تلقائيًا على اليمين رؤية نتيجة كل خطوة بسرعة
نقاط التوقف انقر رقم السطر لإضافة نقطة توقف إيقاف التنفيذ، التتبع خطوة بخطوة
سجل القيم حرك فوق متغير رؤية كيف يتغير المتغير مع الوقت
معاينة التعبير حدد الكود تقييم سريع لتعبير محدد

(2) تصحيح نقاط التوقف

انقر على الجانب الأيسر من رقم السطر لتعيين نقطة توقف. عندما يصل البرنامج إلى ذلك السطر، ��توقف مؤقتًا حتى تتمكن من فحص جميع قيم المتغيرات الحالية:

SWIFT
func calculateTotal(items: [Double], tax: Double) -> Double {
    var subtotal = 0.0
    // Set a breakpoint on this line
    for item in items {
        subtotal += item
    }
    let taxAmount = subtotal * tax
    let total = subtotal + taxAmount
    return total
}
let cart = [29.99, 49.99, 15.00]
let final = calculateTotal(items: cart, tax: 0.08)
print("Total: $\(final)")
💡 نصيحة: في Xcode أو Playgrounds، انقر رقم سطر لإضافة نقطة توقف. عند تشغيلها، تظهر منطقة التصحيح في الأسفل جميع قيم المتغيرات الحا��ية. يمكنك اختيار "متابعة" أو "خطوة فوق" للمتابعة.

▶ مثال: تصحيح Playground عمليًا

SWIFT
// ============================================
// Practice Playground debugging techniques
// Paste this code into a Playground and run it
// ============================================
import Foundation
// 1. Set a breakpoint on the line below to observe variable values
let data: [String: Any] = [
    "product": "Swift Book",
    "price": 39.99,
    "quantity": 3,
    "inStock": true
]
// 2. Step through the following code
let productName = data["product"] as? String ?? "Unknown"
let price = data["price"] as? Double ?? 0.0
let quantity = data["quantity"] as? Int ?? 0
let inStock = data["inStock"] as? Bool ?? false
print("Product: \(productName)")
print("Price: $\(price)")
print("Quantity: \(quantity)")
print("In Stock: \(inStock)")
// 3. Observe the conditional check results
if inStock {
    let totalCost = price * Double(quantity)
    print("Total Cost: $\(totalCost)")
} else {
    print("Item is out of stock")
}
// 4. Use dump to inspect complex data
print("\n=== Debug Info ===")
dump(data)

المخرجات:

TEXT 📖 للعرض فقط
المنتج: كتاب Swift
السعر: $39.99
الكمية: 3
متوفر: true
التكلفة الإجمالية: $119.97

=== معلومات التصحيح ===
▿ 4 أزواج مفتاح/قيمة
  ▿ (عنصران)
    - المفتاح: "المنتج"
    - القيمة: "كتاب Swift"
  ▿ (عنصران)
    - المفتاح: "السعر"
    - القيمة: 39.99
  ▿ (عنصران)
    - المفتاح: "الكمية"
    - القيمة: 3
  ▿ (عنصران)
    - المفتاح: "متوفر"
    - القيمة: true

6. مثال كامل: أداة تصحيح تحليل الدرجات

SWIFT
// ============================================
// Grade analysis tool
// Combines print debugging + assertion checks + formatted output
// ============================================
import Foundation
// 1. Student grade data
let studentName = "Alice"
let scores = [85.0, 92.0, 78.0, 90.0, 88.0]
// 2. Debug assertions to validate data
assert(scores.count > 0, "Score list cannot be empty")
for score in scores {
    assert(score >= 0 && score <= 100, "Score must be between 0-100: \(score)")
}
// 3. Calculate statistics
var total = 0.0
var highest = scores[0]
var lowest = scores[0]
// Set a breakpoint here to observe the loop process
for (index, score) in scores.enumerated() {
    print("[DEBUG] Index \(index): \(score)")
    total += score
    if score > highest { highest = score }
    if score < lowest { lowest = score }
}
let average = total / Double(scores.count)
// 4. Formatted output
print(String(repeating: "=", count: 35))
print("Student: \(studentName)")
print(String(repeating: "-", count: 35))
print("Subject", "Score", "Grade", separator: " | ")
print(String(repeating: "-", count: 35))
for (index, score) in scores.enumerated() {
    let grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D"
    print("Subject \(index + 1)", score, grade, separator: " | ")
}
print(String(repeating: "-", count: 35))
print("Total: \(total)")
print("Average: \(String(format: "%.1f", average))")
print("Highest: \(highest)")
print("Lowest: \(lowest)")
print(String(repeating: "=", count: 35))
// 5. Precondition to ensure reasonable results
precondition(average >= 0 && average <= 100, "Average is outside expected range")
precondition(highest >= lowest, "Highest score should not be lower than lowest")

المخرجات:

TEXT 📖 للعرض فقط
[DEBUG] Index 0: 85.0
[DEBUG] Index 1: 92.0

❓ أسئلة شائعة

س ما الفرق الفعلي بين print وdebugPrint؟
ج print يخرج بتنسيق قابل للقراءة البشرية (مثلاً Alice)، بينما debugPrint يخرج بتنسيق موجه للتصحيح (مثلاً "Alice" مع علامات اقتباس). يمكن للأنواع المخصصة تنفيذ بروتوكولي CustomStringConvertible وCustomDebugStringConvertible للتحكم في كلا الإخراجين.
س هل تُزال assert في وضع الإصدار؟
ج نعم. assert تأخذ مفعولها فقط في وضع التصحيح (-Onone). في وضع الإصدار، يتخطى المترجم تقييم وتنفيذ التأكيدات. لذلك، لا تضع أبدًا منطقًا ذا آثار جانبية داخل assert.
س متى يجب استخدام precondition بدلاً من assert؟
ج precondition تأخذ مفعولها في جميع الأوضاع وهي للأخطاء الفادحة غير القابلة للاسترداد. أمثلة: فحص حدود المصفوفة مسبقًا، وسيط مطلوب يكون nil، فرع لا يجب الوصول إليه منطقيًا. استخدم precondition للتحقق من صحة وسائط API العامة.
س كيف أستخدم نقاط التوقف في Playgrounds؟
ج انقر على الجانب الأيسر من رقم السطر لتعيين نقطة توقف. عندما يصل البرنامج إلى نقطة توقف، يتوقف مؤقتًا. حرك فوق أي متغير لرؤية قيمته. اضغط زر المتابعة أو F6 للانتقال إلى السطر التالي.

📖 ملخص


📝 تمارين

  1. مبتدئ: استخدم print لتوليد جدول ضرب بسيط (1-3)، متحكمًا ��ي التنسيق بـ separator وterminator لإخراج نمط جدولي.
  2. متوسط: اكتب دالة divide(_ a: Double, by b: Double) -> Double، مستخدمًا precondition للتحقق من أن المقسوم عليه ليس 0 وassert للتحقق من أن النتيجة في نطاق معقول. ثم استخدم print مع التنسيق لإخراج النتيجة.
  3. متقدم: حاكِ برنامج سحب من الصراف الآلي. استخدم assert للتحقق من أن مبلغ السحب من مضاعفات 100، وprecondition للتحقق من كفاية الرصيد. استخدم print مع separator وterminator لتنسيق تفاصيل المعاملة بما في ذلك الوقت والمبلغ والرصيد.
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%