วันศุกร์ที่ 31 สิงหาคม พ.ศ. 2555

Memory Management (Day02 @ iDeveloper03)

วันนี้วันที่สองแล้วที่มาอบรม
ยังคงเดินทางไปกลับแบบเดิม
มันไกลดีนะ แต่ว่าชอบจังเลย มหาลัยกว้างใหญ่
ถ้าอยากจะย้ายมาเรียนนี่ทันไหมนะ ปี 4 แล้วจะจบแล้ว
เกิดหลงรักสถานบันกว้างขวาง 5555

ท่าทางจะไม่ทันเสียแล้วหล่ะ 
ไม่เป็นไร เก็บไว้ในใจ เก็บบรรยากาศเอาไว้
และจะไปบ่อยๆ คิดว่าช่วงนี้คงไปเยอะหล่ะ
จบอบรมนี้ก้กอมีท๊อปกันต่อเลย 5555

Memory Management
โดย อาจารย์ภราดา  นาคสุข

ทำไมต้องมีการจัดการกับเรื่องของหน่วยความจำ ???
เพราะว่า ตัวอุปกรณ์เองนั้นมีจำนวนของหน่วยความจำที่จำกัด ไม่มีการซื้อเพิ่มเติมได้
สามารถแก้ไขได้โดยให้ Developer ที่ออกแบบ App นั้นเป็นคนจัดการ วางแผนเอาไว้ ว่าต้องการใช้ ตอนไหนเมื่อไหร่ เลิกใช้ตอนไหนเท่าไหร่ยังไง ซึ่งจะกล่าวถึงวิธีการ ในลำดับต่อไป

ในส่วนของการจัดการ เราจะต้องเรียนใช้ Methods ในการจัดการอยู่ 2 ตัว นั้นก็คือ
+ (id)alloc ใช้ในการจองหน่วยความจำ
- (void) release ใช้ในการคืนหน่วยความจำ

กำหนดให้ Class test เป็นคลาสๆหนึ่งที่สืบทอดคุณสมบัติของ NSObject

test *a = [[a alloc] init];  
         // การสร้างpointer a ที่ทำการชี้ไปยังอ๊อบเจกต์ใหม่ภายใน Class Test
[a doSomething];
         // การเรียก method doSomething ให้กระทำกับอ๊อบเจกต์ที่ a ชี้อยู่
[a release];
         //เป็นการคืนพื้นที่หน่วยความจำ

ตัวอย่างที่จะทำให้เกิดปัญหา ถ้ามีการใช้ Pointer ในการชี้ร่วมกัน

test *a = [[a alloc] init];  
test *ab = a;
         //กำหนดให้ pointer ab ชี้ไปยัง Address เดียวกับที่ pointer a ชี้อยู่
[a doSomething];
[ab doSomething];

[a release];
         //เป็นการคืนพื้นที่หน่วยความจำ

[ab doSomthing];
         //เมื่อสั่งคำสั่งนี้จะทำให้เกิดปัญหา เพราะได้คืนหน่วยความจำที่ชี้อยู่ไปแล้ว จะทำให้เกิดการ Crash ของโปรแกรม ซึ่งจะทำให้ทำงานต่อไม่ได้ โปรแกรมมีปัญหา


Reference Counting : การป้องกันการ Crash ของโปรแกรม

  • Retain Count : ตัวเลขจะบวก 1 ของอ๊อบเจกต์เมื่อถูกทำการอ้างอิง สร้างขึ้นมา
  • Dealloc : จะถูกส่ง message ไปเรียกมาทำลายพื้นที่ที่จองไว้เมื่อ retain count = 0
การส่งข้อความไป Dealloc ไม่ควรทำกับอ๊อบเจกต์โดยตรง

หลักของการนับ Reference Counting
  • Alloc                 +1
  • Copy                 +1
    • แต่จะเป็นการเพิ่มที่เกิดจากออบเจกต์ที่สร้างใหม่อีกตัวหนึ่ง
  • Retain              +1
  • Release            -1
  • Autorelease     -1 แต่จะทำในอนาคต
ตัวอย่างที่ 1
NSObject *obj = [[NSString alloc] init];  //retain = 1
[obj release]; //ทำให้ retain = 0 จะมีการส่งข้อความไปยัง dealloc เพื่อทำการทำลายพื้นที่ที่จองไว้

ตัวอย่างที่ 2
NSObject *obj1= [[NSString alloc] init];  //retain = 1
NSObject *obj2 = [obj1 retain];  //retain = 2
[obj1 release]; //retain = 1
[obj2 release]; //retain = 0 ทำการเรียก dealloc เพื่อทำการทำลายพื้นที่ที่จองไว้

ตัวอย่างที่ 3
-(NSObject *) getObject
{
     NSObject *obj = [[NSObject alloc] init];
     return obj; //จะทำให้เกิด Memory leak นั่นคือการจองไว้แล้วไม่มีการคืน เปลืองพื้นที่ของโปรแกรม
}

ตัวอย่างที่ 4
-(NSObject *) getObject
{
     NSObject *obj = [[NSObject alloc] init];
     [obj release];  //retain = 0 จะทำการทำลายพื้นที่ที่จองไว้
     return obj; //ทำการส่งขยะคืนไป เพราะการสร้างและลบ ค่าที่ส่งไปจะเป็นขยะทำให้เกิดการ Crash
}

ตัวอย่างที่ 5
-(NSObject *) getObject
{
     NSObject *obj = [[NSObject alloc] init];
     [obj autorelease];  //เป็นการสัญญาว่าในอนาคตจะทำการลบพื้นที่ที่จองเอาไว้
     return obj; //เป็นวิธีการที่ดีที่สุด ไม่เปลือง ไม่ได้ขยะ มีการคืนในอนาคต แต่ในปัจจุบันมีการใช้ ARC เข้ามาช่วยในการจัดการกระบวนการ Retain & Release แล้ว ทำให้ง่ายต่อการ Developer
}

ตัวอย่างที่ 6
-(void)printHello
{
     NSString *string;
     string = [NSString stringWithFormat : @"Hello"];
              //NSString เป็นชื่อคลาสที่ Pointer string ชี้อยู่
             //stringWithFormat เป็น Method ในการจัดเก็บข้อความ
     NSLog(@"%@",string);
            //เป็น Method ในการแสดงข้อความที่เก็บอยู่ในตัวแปล String
            //NSLog เปรียบเสมือนการสั่ง Printf
}
** ในตัวอย่างนี้ไม่ต้องมีการสั่ง Retain & Release เนื่องจาก stringWithFormat , NSLog เป็นส่วนของ Convenience constructors ไม่ต้องทำการคืนหรือจองหน่วยความจำ **

  • การจองหน่วยความจำจะเกิดขึ้นกับการ Alloc Copy Retain Release autorelease เท่านั้น
  • ข้อยกเว้น ในการ dealloc ไม่สามารถเข้าถึงออบเจกต์โดยตรงได้ แต่สามารถเข้าถึง Super Class โดยตรงได้ สามารถกำหนดโดย [super dealloc];
การแสดงสถานะที่ออบเจกต์นั้นอยู่ทำได้ผ่านการส่งข้อความดังนี้
NSLog (@"%@ called : %@", NSStringFormSelector(_cmd),self);
  • NSStringFormSelector(_cmd) : เป็นการบอกว่าออบเจกต์นั้นอยู่ใน Methods ใด
  • Self : เป็นการบงบอกว่า เรียนออบเจกต์ใดออกมาใช้อยู่ เปรียบเสมือน this ในภาษาจาวา
Automatic Reference Counting (ARC)
** สามารถเลือกได้ในขั้นตอนแรกของการสร้างโปรเจค **
  • ARC เป็นฟีเจอร์ของคอมไพเลอร์ที่ช่วยในการจัดการหน่วยความจำ (Retain & Release)
  • คอมไพเลอร์จะแทรกโค๊ดให้อัตโนมัติ
  • ไม่ลดความเร็วในการทำงานของโปรแกรม
  • ในการประการออบเจกต์ต้องทำการระบุว่าใครเป็นเจ้าของ
    • เจ้าของ (Owner) จะเป็นออบเจกต์ชนิด Strong Relationship
      • จะมีนับเมื่อเริ่มชี้ไปยังออบเจกต์ (alloc)
    • อ้างอิง จะเป็นออบเจกต์ชนิด Weak Relationship
      • จะไม่มีการนับ เมื่อทำการชี้ไปยังออบเจกต์ ซึ่งจะทำให้ออบเจกต์ที่อ้างอิงอยู่ถูกทำลายลงไปเมื่อไหร่ก็ได้ แต่เมื่อถูกทำลายตัวออบเจกต์จะถูกกำหนดค่าเป็นค่า nil (ค่าว่าง) 
      • การกระทำนี้อาจจะทำให้ข้อมูลที่เราต้องการเรียกใช้เกิดความผิดเพี้ยน
      • ไม่ว่าอย่างไรนักพัฒนาจำเป็นจะต้องตรวจสอบการเรียนใช้หน่วยความจำของตัวเองอยู่ดี เพราะถ้ามีการผิดพลาด ARC จะช่วยไม่ใช้โปรแกรม Crash เท่านั้น
  • หากการประกาศตัวแปรไม่ระบุบว่าเป็น __strong หรือ __weak จะถือว่าเป็น __strong
  • ข้อควรระวัง
    • การกำหนดให้เป็น __weak จะออบเจกต์อ้างอิงสามารถถูกทำลายเมื่อใดก็ได้
    • _strong อาจจะทำให้เกิด Circular Ownership (deadlock)
      • deadlock คือการค้างอยู่ในระบบ มีการจองพื้นที่เป็นของกันและกัน ไม่สามารถลบทิ้งออกจากระบบไปได้ เช่น A จองพื้นที่เดียวกันกับ B และ A กับ B เป็นของกันและกัน สามารถแก้ไขได้จากการออกแบบโปรแกรมเท่านั้น
ตัวอย่างที่ 1.1 เรียกใช้ ARC
กำหนดให้ myClass เป็น Class ที่สืบทอดมาจาก NSObject

myClass *var1 = [[myClass alloc] init];  //retain = 1
myClass * __strong var2;

var2 = var1;
   // แสดงว่า var2 มาขอเป็นเจ้าของร่วมกับ var1


ตัวอย่างที่ 1.2 ใช้แบบไม่มี ARC
myClass *var1 = [[myClass alloc] init];  //retain = 1
myClass * var2 = nil;  //

if(var2 != var1)
{
     [var2 release]; 
     var2 = [var 1 retain]; //retain = 2
}
[var2 release];  //retain = 1
[var1 release];  //retain = 0

ตัวอย่างที่ 2.1 ใช้ ARC
myClass *var1 = [[myClass alloc] init];  //retain = 1
myClass * __weak var2;

var2 = var1;

[var1 release]; 
[var2 doSomething]; 
   //รันผ่าน แต่จะได้ค่าของ var2 ที่ผิดเพี้ยนไป เพราะพื้นที่ที่จองไว้ถูกทำลายไปแล้ว อาจจะไปได้อย่างอื่นมาแทน

ตัวอย่างที่ 2.2 ใช้แบบไม่มี ARC
myClass *var1 = [[myClass alloc] init];  //retain = 1
myClass * var2 = nil;  //

if(var2 != var1)
{
     [var2 release]; 
     var2 = [var 1 retain]; //retain = 2
}

[var1 release];  //retain = 0
[var2 doSomething]; 
   // รันผ่าน แต่จะได้ค่าของ var2 ที่ผิดเพี้ยนไป เพราะพื้นที่ที่จองไว้ถูกทำลายไปแล้ว อาจจะไปได้อย่างอื่นมาแทน

ตัวอย่างโปรแกรมที่ทำให้เกิด Deadlock
@interface Department : NSObjcet
{
    Person* __strong head;
}
@end

@interface Person : NSObject
{
     Department* __strong dept;
}
@end


ปล. จบแล้วจร้า 1 ส่วนของวันที่ 2
ปล. หากมีข้อผิดพลาดประการใด ขออภัยไว้ ณ ที่นี้ด้วยจร้า

ไม่มีความคิดเห็น:

แสดงความคิดเห็น

หมายเหตุ: มีเพียงสมาชิกของบล็อกนี้เท่านั้นที่สามารถแสดงความคิดเห็น