آيفونآيبودآبل

سلسلة دروس برمجة تطبيقات الآيفون (3)

آيفونآيبودآبل

1276627696_14277791302d16557a6fbo

الدرس الثالث من سلسلة دروس برمجة تطبيقات الآيفون ، في البداية عندما نعمل على تصميم Class في مرحلة التصميم النظري نحاول استخلاص الآتي:

-   اسم الـ class

-   نحدد إلى أي superclass  هي تنتمي وهي الـ class  الأم وفي الغالب ستكون NSObject

-   نحدد المواصفات الموجودة فيها وهي التي تحدد المتغيرات variables

-   نحدد الأفعال التي سوف تعملها وهي actions  أو functions أو  methods

الـ classes المخصصة

في برنامج الـ xcode  عند إنشاء أي class سأحتاج إلى شيئين :

1-    ملف الرأس أو مايسمى header file

2-    ملف التطبيق أو مايسمى implementation file

في ملف الرأس أو الـ header  يكون فيه أسماء المتغيرات وأسماء functions or methods ويحفظ بصيغة h. وفي الملف الثاني m. يكون فيه الكود البرمجي لأي method وأيضاً set and get للمتغيرات وملف implementation أقل وضوح من header  وذلك لكثرة سطور الكود فيها لكن ملف header  هو المرجع الذي يرجع إليه عند الحاجة لأي متغير أو method في الـ class

فإذا أردت أن أعرّف class  في ملف header حيث person هي اسم الـ class :

#import <Foundation/Foundation.h>

@interface person : NSObject

{

//هنا سنكتب أسماء المتغيرات

NSString *name; //  stringهنا عرفت متغير من نوع

int age; //int هنا عرفت متغير من نوع عدد صحيح

}

// methodsهنا سنكتب تعريف الـ

– (NSString *)name; // nameهذه دالة ترجع لي القيمة المحفوظة في

– (void)setName:(NSString *)value; // nameهنا تعطي قيمة للـ

– (int)age; //  ageترجع قيمة

– (void)setAge:(int)value;//  ageتعطي قيمة للمتغير

– (BOOL)canLegallyVote;

//  boolean هذه ترجع إما بيمكن أو لايمكن بقيمة صحيح أو خطأ أو مانسميه

– (void)castBallot;//هذه الدالة تعطي الحق للشخص بالتصويت

@end

سنلاحظ الآن أن المتغيرات والـ methods  الموجودة في ملف header  لابد أن تكون هي نفسها الموجودة في ملف implementation :

#import "person.h"

@implementation person

– (int)age {

return age;

}

– (void)setAge:(int)value {

age = value;

}

-(void)setName:(NSString *)value{

Name=value;

}

– (BOOL)canLegallyVote {

return ([self age] >= 18);

}

– (void)castBallot {

if ([self canLegallyVote]) {

// do voting stuff

}

else {

NSLog (@"I’m not allowed to vote!");

}

}

//… وغيرها من الطرق

@end

ما نلاحظة :

  • أن علينا في البداية أن ننادي ملف header ثم نكتب @implementaion لإسم الـclass  المختار ثم بعد ذلك نأخذ كل method ونكتب الكود الخاص فيها.
  • ولابد أن كل method  موجود في ملف header  يكون له implementation في ملف implementation ولكن ليس لكل method موجودة في ملف implementation  تكون موجودة في ملف header .
  • نلاحظ أيضاً أن قبل كل method  يوجد علامة ناقص -  وفي بعض الأحيان سنستخدم علامة الموجب + الفرق بين هاتين العلامتين في الناقص هذا يعني أنا هذه method سوف تستخدم في Object أنشأ من هذه class ولكل object  يكون له هذه method لكن عندما نستخدم علامة الموجب فهذا يعني أن هذه الـ method سوف تستخدم مع كل الـ objects من نفس class
  • نلاحظ شيء آخر وهو self وهو يقوم مقام this  الموجود في لغتي java and c++ حيث أنه ينادي المتغيرات أو methods  الموجودة في نفس class كما تعامل مع المتغير age  ومع الـ method canLegallyVote.

حياة object :

بالنسبة لأي object  سنرى كيف ننشئه وكيف إدارة الذاكرة بالنسبة للـ objects وأيضاً كيف نحذف الـ object

عند إنشاء أي object  هناك خطوتين:

-   إيجاد مكان في الذاكرة تحفظ هذا الـ object

-   إعطاء قيم مبدئية للمتغيرات التابعة لهذا الـ object

alloc+ :هي method تقوم بمعرفة حجم الذاكرة التي يحتاجها هذا object

init- : وهي method خاصة بكل object على حده وتقوم بإعطاء قيم مبدئية للمتغيرات

لذلك معادلة تكوين Object هي :

تكوين object = إيجاد مكان في الذاكرة + إعطاء قيم مبدئية

اذا رجعنا إلى ملف h.  وملف m. في المثال السابق فلو أردنا أن ننشيء object  سنقول:

person *person=nil;

person = [[person alloc] init];

هنا init عبارة عن method  تعطي قيم مبدئية للـ Object الجديد الذي تم إنشائه.

سنرجع إلى ملف .m حتى نكتب  implementation  للـ init method  وسنكتب التالي:

-(id)init {

if (self = [super init]) {

age = 0;// هنا يعطي قيمة مبدئية للعمر بقيمة 0

name = @"Ahmd"; // هنا يعطي قيمة مبدئية للاسم بأحمد

} return self;}

في البداية قمنا بكاتبة اسم الـ method والقيمة المرجعة وهي الـ object  نفسه في السطر الثاني عندما نادينا self وقلنا أنها تساوي [super init] .

كما نعلم أن أي class لها superclass وفي مثالنا  الـ superclass هي  NSObject فعندما ناديت هذا السطر هو بمثابة إعطاء قيم مبدئية للـ object المنشأ بالنسبة للـ superclass وفي بعض الأحيان يكون هناك خطأ في هذه الحالة سترجع القيمة بـ null أو قيمة غير صحيحة ولن تدخل على باقي الكود وهذه حالة نادرة أن تحدث لكن لأخذ الحيطة نكتب هذا السطر ، وفي النهاية سنرجع الـ object نفسه بالقيم المبدئية المعطاة.

يمكن للـ class كتابة الـ implementation  للـ init method بأكثر من طريقة بالإضافة إلى الطريقة في  الأعلى ويمكن كتابة كل الطرق في implementation ولن يكون هناك أي مشاكل.

من هذه الطرق:

– (id)init;//هنا لن يكون للإسم أي قيمة ولا العمر

– (id)initWithName:(NSString *)name;// هنا تعطي قيمة للإسم أما العمر فقيمته صفر

– (id)initWithName:(NSString *)name age:(int)age;//هنا تعطي قيمة للإسم وللعمر

في الملف الذي ننشيء فيه الـ object وننادي فيه الـ methods سيكون كالتالي:

person *person = nil;

person = [[person alloc] init];

[person setName:@" Ahmad Mohammad";

[person setAge:23];

[person castBallot];

إشارة العد (reference counting) :

عند إنشاء أي object ننادي alloc method وهذه الـ method تعمل على حجز مكان في الذاكرة لهذا الـ object في حالة أننا لم نعد نحتاج لهذه الـ object يجب علينا حذفه حتى لايعمل ثقل على الذاكرة ويتم ذلك من خلال الـ dealloc method لكنها لا تنادي بشكل صريح بل نرجع في ذلك إلى  reference counting وهو يعرف بالمتغير retain count ومعرف بالــ NSObject class فمتى كان هذه المتغير أكبر من الصفر فهذا يعني أن الـ object  مازال حي ولا نحتاج إلى حذفه.

عند مناداة alloc method تعمل على إعطاء الـ retain count القيمة 1 وفي كل مرة أنادي الدالة

–retain

فهي تزيد من قيمة retain count بواحد وعند مناداة الدالة

-release

فهي تنقص من قيمة retain count بواحد وعند وصول قيمة retain count للصفر ينادي بشكل تلقائي الدالة dealloc وبمجرد مايقوم بحذف الـ object لايمكن الرجوع واستخدامة من جديد.

فنضيف للكود السابق السطر التالي [person release]; بحيث يكون بهذا الشكل:

person *person = nil;

person = [[person alloc] init];

[person setName:@" Ahmad Mohammad";

[person setAge:23];

[person castBallot];

[person release];

فلو كتبنا بعد السطر الأخير مناداة لأي method سيعلق البرنامج ولن ينفذ الأمر وحتى لايحدث هذا نعطي قيمة للـ object بـ null ونضيف السطر التالي بعد أي release كخطوة إحتياطية لا أكثر

person = nil;

كما قلنا سابقاً أن dealloc method ستنادى بشكل تلقائي لكن يمكن مناداتها بشكل صريح ويمكن أيضاً التعديل عليها وكتابة implementation  خاص فيها كما في المثال:

-(void)dealloc{

//هنا نكتب مانحتاج إلى حذفه بشكل صريح ويكون ضرورياً

[super dealloc];

}

في السطر الأخير نادينا dealloc method للـ super class بشكل صريح,كما نادينا alloc  لها سننادي أيضا  dealloc ولانحتاج إلى ذلك إلا اذا عملنا الـ dealloc method implementation في الـ class .

ملكية الـ Object  :

المتغيرات الموجودة في أي class هي من الملكيات التابعة للـ object  عند إنشائه وتحمل القيم المبدئية إلا اذا تم التعديل عليها يتم التعديل على أي متغير من خلال الـ set method  ففي هذه الدالة يجب التأكد من أن القيمة الجديدة لا تساوي القيمة القديمة حتى لانسبب أي مشاكل وخاصة مع string فعندما لاتساويها يجب أن نعمل release لهذا المتغير حتى نتأكد من أنه لا يوجد كود آخر يستخدم هذا المتغير في نفس هذا الـ object أيضا من خلال هذه الخطوة نتأكد من أن المتغير لم يعمل له release  من قبل وأنه مازال موجوداً  ثم نعمل على تغيير retain count إما من خلال retain  أو من خلال copy  وعند استعمال retain فهو سيزيد الـ retain count  بواحد إلى القيمة الموجودة فيه بعد أن نقصت بواحد بسبب الـ release method أما في copy  سيبدأ العد من جديد بقيمة 1 وتكتب setName method في المثال السابق بهذه الطريقة:

– (void)setName:(NSString *)newName{

if (name != newName)

{ [name release];

name = [newName retain];

// name = [newName copy]; أو يمكن استبدال السطر الأخير بهذا السطر

}

تحرير متغير الـ object :

يجب أن نضع في إعتبارنا أن أي retain لابد أن يقابلها release ليكون الكود بشكل صحيح وحتى لاتظهر لنا أخطاء لانعلم مصدرها فكما استخدمنا retain مع الـ name سنستخدم release ونضع ذلك في dealloc method  كالتالي:

-(void)dealloc{

[name release];

[super dealloc];

}

التحرير التلقائي (Autorelease)

سأبدأ في شرح المشكلة أولاً في الكود التالي:

-(NSString *)fullName{

NSString *result;

result = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];

return result;

}

نلاحظ أن هذه الـ method تعمل على إعطاء إسم ثم إرجاع هذه الإسم في داخل الـ function عملنا على إنشاء object جديد result من نوع NSString وعملنا alloc لهذا الـ Object وحجزنا له مكان في الذاكرة ثم أرجعت قيمته وخرجت من الـ method بدون ماأعمل على حذفه وهذا سيسبب مشكلة من ناحية الذاكرة وخاصة اذا كثر استخدام هذه الـ method مما قد يعطل الذاكرة لدي قد يقول قائل سنضع release method كما تعلمنا من قبل وسيكون الكود بهذا الشكل:

-(NSString *)fullName{

NSString *result;

result = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];

[result release];

return result;

}

لكن لو لاحظنا أن القيمة أحتاجها لإرجاعها واذا عملت release كما في المثال السابق ستنحذف ولن يكون هناك قيمة حتى أرجعها. حسنا الحل هنا استخدام الـ autorelease method :

-(NSString *)fullName{

NSString *result;

result = [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];

[result autorelease];

return result;

}

عندما أنادي autorelease method فإنها تعمل release للـ object في المستقبل عند الحاجة لذلك وهي تعطي للـ object بعض من الوقت ليستفاد منه قبل الحذف وهي الطريقة الأسلم لتحرير أي object جديد داخل method.

في كثير من الـ methods وخاصة الجاهزة تقوم بعمل autorelease لأي  object منشأ منها في بعض الأحيان قد أحتاج إلى هذا الـ Object فعلي في هذه الحالة أن استخدم retain method وعند استخدامها سأكون مسؤول أيضاً عن استخدام release method بالمقابل.

خصائص الـ objective-c

-  من خصائصها أنها تعطي إمكانية الدخول على attributes التابعة لأي class والمقصود بالـ attributes هي الـ variables أو instance variable.

-   تعطي إمكانية التعديل على هذه attributes من خلال setter/getter methods أيضا تعطي الحرية بتحديد هل الكائن للقراءة فقط أم للقراءة والكتابة أي التعديل وأيضا تسمح بالتحكم بالذاكرة من خلال release/retain/autorealse/alloc.

فعندما أريد إضافة بعض الخصائص سواء للمتغيرات أو methods يمكن إضافة ذلك في ملف الـ.h

كما في المثال الذي كنا نعمل عليه في السابق وهو التالي:

#import <Foundation/Foundation.h>

@interface Person : NSObject {

// instance variables

NSString *name;

int age;

}

// method declarations

– (NSString *)name;

– (void)setName:(NSString *)value;

– (int)age; – (void)setAge:(int)age;

– (BOOL)canLegallyVote;

– (void)castBallot;

@end

يمكن استبدال كود تعريف  الـ Methods بالتالي وإعطاء كل method خصائص معينة:

#import <Foundation/Foundation.h>

@interface Person : NSObject {

// instance variables

NSString *name;

int age;

}

// property declarations

@property int age; / /هنا قلت أن المتغير من نوع صحيح بدون خصائص اضافية للدالة

@property (copy) NSString *name;

هنا أعطيك خاصية اضافية وهي أن المتغير يعمل له دائما  copyولا يعمل له // retain

@property (readonly) BOOL canLegallyVote;

هنا قلت أن method للقراءة فقط وعرفتها بتعريفها العادي//

– (void)castBallot;

هنا ابقيت على تعريف method كما هو بدون أي اضافات//

@end

في السطر الأول من الـ property لم أعطي الage method أي خصائص إضافية وهذا يعني أن الـمتغير read-write بشكل تلقائي.

في السطر الثاني استخدمت copy وهذا يعني أنه عند استخدام الـ name سيعمل له copy في كل مرة ولايمكن أن يكون موجود سابقا يمكن استبدال copy بـ retain أو assign.

أيضا عندما أعطي المتغير خاصية الـ property فإني استخدمه في البرنامج كالتالي:

person.age =23;

وبالرجوع إلى ملف .m فمن الكتابة الأولى يكتب الكود بهذه الطريقة:

@implementation person

– (int)age {

return age;

}

– (void)setAge:(int)value {

age = value;

}

– (NSString *)name {

return name;

}

– (void)setName:(NSString *)value {

if (value != name) {

[value release];

name = [value copy];

}

– (void)canLegallyVote {

return (age > 17);}

@end

يمكن استبدال هذه الكود بالتالي:

@implementation person

@synthesize age;

@synthesize name;

– (BOOL)canLegallyVote {

return (age > 17);

}

@end

المقصود هنا بـ synthesize  هي بدل كتابة setter/getter methods فهي تغني عنها لكن يمكن أن أجمع بين الإثنين في نفس الكود ولن يكون هناك مشاكل.

انتهى الدرس الثالث بحمدالله ومنته …

دمتم بود 🙂

الوسوم

14 رأي على “سلسلة دروس برمجة تطبيقات الآيفون (3)”

  1. يعطيكي العافية

    بانتظار الجزء الرابع

    واذا كانت في فيه امثلة تطبيقية يا ريت تحطيها نستفيد منها

    وشكرا

    1. ما شاء الله
      برامجك معروفة اخوى و لا تخفى
      و اتمنى ان نرى منك دروسا اخوى طارق
      ولكن تكون مبسطة
      وعندى سؤال هل تستخدم الماك لتطوير البرامج ام تستخدم الويندوز ؟
      و شكرا جزيلا لك اخوى على دريك الرائع وفقك الله 🙂

  2. ماشاء الله تبارك الله عليك

    انا محتاج لهذه الدروس وطريقة شرحك رائعة

    ياليت ماتوقفي السلسلة وتستمري

    واتمنى لو تصدري كتاب  

  3. ما شاء الله

    من المتابعين للدروس

    حقيقة بأننا نستفيد من هذه الاطروحات

    الف الف شكر لكم .. وبانتظار البقية من الدروس

  4. أتمنى . . أتمنى . . أتمنى أن يكون الدرس فيديو حتى نفهم أكثر ولك جزيل الشكر والعرفان

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *