Archive

Archive for April, 2014

วิธีการอ่าน PWM Signals จาก Receiver ด้วย Arduino ขั้นพื้นฐาน

เรามาเรียนรู้วิธีการอ่าน สัญญาณ PWM จาก Receiver กันครับ

จากที่เราทราบกัน นั้น ขาสัญญาณที่ใช้คบคุม servo นั้นส่งสัญญาณออกเป็น PWM และสามารถใช้ควบคุม servo ได้โดยตรง หากเราต้องการใช้สัญญาณนี้เราสามารถอ่าน จากขาสัญญาณเหล่านี้ได้ด้วย Arduino โดยตรง
IMG_20140425_222930

ภาพบนเป็นภาพลักษณะการใชงานทั่วไป ครับ เนื่องจาก board ที่เราจะใช้นั้นเป็น Arduino UNO จะมีขาสัญญาณ PWM อยู่สามารถอ่านค่าได้โดยตรง ด้วยคำสั่ง pulseIn(…) ดังนั้นเพื่อทำความเข้าใจ ก็จะลองทดสอบอ่านค่าจากขาสัญญาณ จาก receiver ด้วย Arduino ดูนะครับ

อุปกรณ์ที่จะใช้

เราจะใช้ขา pin 9 ต่อกับ ช่องสัญญาณที่หนึ่งของ Receiver ดังรูป แล้วให้ Power เข้าที่ ช่องสัญญาณที่ 6 แล้วเราลองทดสอบกันด้วยโปรแกรมสั้น ๆ ตามนี้ครับ

int ch1 ; // to keep channel values

void setup()
{
	pinMode(9,INPUT); // set out input pin as such 
	Serial.begin(9600); 
}

void loop()
{
	ch1 = pulseIn(9,HIGH,25000); // Read the pulse width of the channel
	Serial.print(“channel data : “); // print value of
	Serial.println(ch1) ; // the channel
	
	delay(100);
}

puleIn() function ต้องการ 3 arguments ตัวแรกก็คือ หมายเลข pin ที่เราต้องการให้สัญญาณ pulse เข้ามา ตัวที่่ 2 คือ pulse ‘HIGH’ หรือ ‘LOW’ ที่เรากำลังสนใจอยู่ และสุดท้ายก็เป็น เวลาที่เรากำหนดให้ function รออ่านค่า เรียกว่า time-out
ค่าที่ return ให้ของ puleIn() ก็คือ ความยาวของ pulse มีหน่วยเป็น microseconds และนี่ก็คือวิธีการอ่านสัญญาณ PWM ซึ่งเป็นวิธีเดียวกับที่ servo อ่านสัญญาณนี้
ค่าที่ได้เราให้แสดงออกทาง terminal เมื่อเราทำการประมวลผลโปรแกรมนี้เราควรจะได้ ตัวแสดงผลขึ้นมาที่หน้า terminal ตัวเลขนี้ค่าควรจะอยู่ระหว่าง 1000 – 2000 และเมื่อเราขยับ joy stick ที่เชื่อมโยงกับช่องสัญญาณนี้ ตัวเลขก็ควรจะเปลี่ยนแปลงให้เห็น

IMG_20140425_222939
ครับ เท่านี้ก็ถือว่า เป็นการทดสอบที่ สมบูรณ์ และสามารถใช้งาน ได้แล้ว  ในเรื่องนี้ยังไม่จบนะครับนี่เป็นจุดเริ่มต้นของการเรียนรู้ในขั้น Advance ต่อไปครับ

ธีระพงษ์ สนธยามาลย์

s.teerapong@gmail.com

เรื่องของ PWM และ PPM

อันที่จริงความรู้อะไรเกี่ยวกับทางนี้ผมก็ไม่ค่อยมีนะครับ หา หา เอาจาก internet พอดีว่ามีความสนใจเล่นเจ้า Arduino เป็นงานอดิเรก [ เกือบเป็นงานหลักแล้วตอนนี้ ] ในการทำโครงงานบางอย่างต้องใช้พื้นฐานความรู้ความเข้าใจในลักษณะสัญาญที่ต้องนำสัญาณนั้นมาใช้บ้างไว้เป็น concept พื้นฐานในการคิดในเรื่องการใช้งานต่อไป

ส่วนตัวผมนอกจากจะสนใจงานทางด้านนี้แล้วยังสนใจและเล่น เครื่องบินบังคับ หรือบ้านเราก็เรียกว่า R/C ครับ  ที่กล่าวมานี้ ก็จะบอกว่ามีเครื่องมือชนิดหนึ่งที่เราใช้ในการควบคุม เครื่องบินก็คือ Radio Control และสัญญาณอุปกรณ์นี้ต้องใช้ในการควบคุมระยะใกล้ก็คือคลื่นวิทยุโดยที่มีตัวรับเป็น Receiver เพื่อรับและ decode สัญญาณออกมาควบคุม Servo ทำให้เราต้องรู้จัก PWM และ PPM กันไงครับ

heli_radio_components-1-5

PWM และ PPM นั้นเป็นคำที่เห็นกันบ่อย ๆ ในวงการ R/C   เจ้า PWM นั้นย่อมาจาก Pulse Width Modulation และ PPM นั้นย่อมากจาก Pulse Position Modulation อุปกรณ์ที่ใช้ สัญญาณ PWM ในการควบคุม ก็คือ Electronic speed controls หรือ ESC และ servos. PWM เป็นการ encode ข้อมูลด้วยการใช้ความกว้างของ pulse (ด้วยระยะเวลาต่างๆ กัน)

ในระบบ digital electronic นั้นเราก็จะรู้จัก ค่าของ binary ซึ่งมีค่า 1 และ 0 เราจะแทนค่า 1 ด้วย  ‘on’ และทนค่า 0 ด้วย ‘off’ ซึ่งตัวอย่างของ การแสดงสัญาณนี้ก็คือ swtich ไฟ เปิดไฟ ก็คือ ‘1’ ปิดไฟก็คือ ‘0’ นั่นเอง  แต่ในกรณีของ สัญญาณ PWM/PPM นั้น ก็เช่นเดียวกัน ค่า voltage ใด ๆ แทนค่า 1 และ กลับกันคือ 0 แทนด้วย  0 voltage แต่ในทางการใช้งานควบคุมนั้น แค่ on/off ไม่เพียงพอ  ซึ่งนะจุดนี้จึ่งเป็นที่มาของ pulse width

ธีการในการ เข้ารหัสข้อมูล ก็คือการใช้เวลาที่ pulse อยู่ในสถานะ on ในกรณีของ R/C electronics นั้นช่วงเวลาจะอยู่ในรหะว่าง 1-2 milliseconds. (หรือ 0.5 – 2.5 ms )

อุปกรณ์ servo หรือ ESC นั้นจะค่อยดู pulse และจะนับเวลาเมื่อตรวจพบ pulse นี้ จะหยุดนับเวลาเมื่อ pulse หมด pulse ดังนั้นเวลาทั้งหมดที่ pulse มีสถานะเป็น on จะเป็นตัวบอกว่า servo จะหมุนไปแค่ไหน ตัวอย่างเช่น pulse on เป็นเวลา 1 ms จะทำให้ servo หมุนมาทางด้าน ซ้ายสุด แต่ถ้าเวลาเป็น 2ms จะทำให้ servo หมุนไป ซ้ายสุด
Screen shot 2014-04-24 at 14.13.15

รูปแสดง ความกว้างของ pulse ต่อการเคลื่อนที่ของ servo
( ในรูปใช้ ห้วงเวลาระหว่าง 0.5 – 2.5 ms แต่ในคำอธิบายใช้ 1-2 ms )

ทั่วไปแล้ว อุปกรณ์ R/C ทั้งหลาย สัญญาณหนึ่งวงรอบจะใช้ 20 ms ซึ่งเราจะเรียกห้วงเวลานี้ว่า frame ในหนึ่ง fame จะประกอบด้วย ช่วงที่ pulse มีสถานะเป็น high (1-2 ms) และช่วงที่ pulse มีสถานะเป็น low ดังรูปด้านล่าง
Screen shot 2014-04-24 at 14.16.03

ถึงแม้ว่า fame มีความกว้าง 20 ms ก็ตาม ส่วนที่สำคัญของ pulse ก็คือห้วงเวลาที่ pulse มีสถานะเป็น on ซึ่งก็ประมาณ 1-2ms (0.5 – 2.5 ms) แม้ว่าช่วงที่ pulse มีสถานะเป็น off หรือ low นั้นจะไม่ได้ใช้งานอะไร ก็ยังคงเก็บไว้ และ frame ขนาด 20 ms นี้ถือว่าเป็นเวลาที่ดีที่สุดแล้ว หากเพิ่มเวลาให้มากกว่านี้ออกไป อาจทำให้เสียการควบคุม servo ได้ เช่น เสีย holding power อาจทำให้ servo เกิดอาการ กระตุกเป็นระยะได้

ต่อไปนะครับ ว่าทำไมถึงจะต้องรอจนถึง 20 ms ส่วนนี้เป็นข้อกำหนดของ อุปกรณ์ R/C และคำอธิบายจะทำให้เราเข้าใจ PPM ต่อไป
PPM ย่อมาจาก Pulse Position Modulation พูดง่ายๆ ก็คือ PPM นั้นเกิดจากการนำ PWM มาเรียงต่อกัน ดังภาพ

Screen shot 2014-04-24 at 14.16.11

นอกจากเหตุผลในเรื่อง ของการรักษา holding power แล้ว เหตุผลของการใช้ 20ms คือการที่จะทำให้เราสามารถนำเอา PWM มาจัดวางต่อกันใน frame เดียวกันได้ อย่างที่ได้กล่าวไว้ข้างต้นว่า ห้วงเวลาที่ pulse อยู่สถานะ on นั้นเป็นส่วนที่เราใช้ประโยชน์ เราก็นำห้องเวลาของ pulse on มาจัดเลียงในหนึ่ง frame เราสามารถใช้วิธีการในการส่งข้อมูลหลายๆ channels ในห้วงเวลา 20ms ได้พร้อม ๆ กันจิงไหมครับ
Screen shot 2014-04-24 at 14.16.19

เช่น คิดง่าย ๆ ว่า หาก เครื่องส่งวิทยุเป็นแบบช่องสัญญาณเดียวจะส่ง 1 PWM ในหนึ่ง frame (20ms) แต่ถ้า วิทยุเป็นแบบ 8 channels เราก็สามารถซอยห้วงเวลา 1 frame เพื่อที่จะจัดวาง pulse ของ 8 PWM ได้ ซึ่งอาจจะให้ channels ละ 1-2 ms ก็แล้วแต่การใช้งาน

ถึงเวลานี่้คงได้ concept ในเรื่องนี้เพื่อการใช้งานต่อไปครับ รายการด้านล้างจะแบ่งให้ เห็นว่าอถปรณ์ใดใช้ PWM และอุปกรณ์ใดใช้ PPM ครับ

R/C Devices ที่ใช้ PWM
  -servo
  -electronic speed controller
  -R/C switches
  -R/C lights
  -R/C receivers
  -Data loggers
  -Failsafe's
  -Autopilot/Stabilization systems
  -Servo Controller

R/C Devices ที่ใช้ PPM 
  -R/C transmitters
  -R/C receivers
  -Autopilot/Stabilization systems
  -PCTx

สำหรับเรื่องนี้ผมเอาไว้เป็นพื้นฐานในการใช้งานในเรื่องของการ อ่านข้อมูลจาก receiver ของ R/C ครับ
จบดีกว่า นะ

Categories: Arduino

Objective-C Note ภาคต่อ 3

ใกล้หมดแล้วครับมาต่อกันเลย

ความเข้าใจในการใช้ nil [ Calling methods on Nil]

ใน Objective-C นั้น nil object นั้นมี การทำงานเหมือน NULL pointer ในหลายๆ ภาษาที่แตกต่างไปก็คือเราสามารถ เรียก method ของ nil object โดยไม่ทำให้โปรแกรมล่มหรือทำให้เกิด exception ได้ [” จิงดิ “]

เทคนิคนี้ถูกนำไปใช้ใน Frameworks หลากหลายวิธีการ แต่ความหมายที่ต้องการให้ทราบก็คือในขณะนี้ก็คือเราไม่จำเป็นต้องตรวจสอบ object ก่อนการเรียกใช้ method ว่า object นั้น จะเป็น nil หรือไม่ หากเราเรียก method ของ nil object จะได้ค่า nil เป็นค่า return

เราลองมาดูการปรับปรุง dealloc ให้ดีขึ้นอีกนิส

-(void) dealloc
{
	self.caption = nil; 
	self.photographer = nil;
	[super dealloc];
}

code ส่วนนี้ทำงานได้เนื่องจากเมื่อเรากำหนดค่า nil ให้ instance variable setter จะทำการ retain ค่า nil (แปลว่าว่างเปล่า)และreleaseค่าเก่าออกด้วยวิธีการนี้จะทำให้ variable หมดโอกาสที่จะชี้สุ่มไป เรื่อยเปื่อย

การที่เราใช้ รูปแบบ self. ก็หมายถึงเรากำลังใช้ setter และให้ memory management ของระบบ จัดการเรื่องการคืน memory ถ้าหากเราใช้แบบกำหนดค่าให้โดยตรง(ตัวอย่าง codeด้านล่าง) จะทำให้เกิด memory leak ได้

// incorrect. Causes a memory leak.
// use self.caption to go through  setter 
 
caption = nil;

Categories

categories เป็น คุณลักษณะที่สำคัญอีกอันหนึ่งของ Objective-C โดยที่ Categories นั้นจะทำให้เราสามารถที่จะเพิ่ม method ให้กับ class ที่มีอยู่โดยที่ไม่ต้องทำ sublassing หรือ รู้รายละเอียดเกี่ยวกับการ implementation ของ class นั้น ๆ เลย
ถือว่ามีประโยชน์มากเนื่องจากเราสามารถที่เพิ่ม method ให้กับ built-in object( object ของ class ใน framework ของ cocoa เอง ) เช่นหากเราต้องการเพิ่ม method ให้กับ instance ของ NSString ใน application ที่เรากำลังทำอยู่ เราแค่เพิ่ม category. เราไม่จำเป็นต้องรู้ทุกอย่างเพื่อทำ subclass ขึ้นมา

ตัวอย่าง ถ้าต้องการเพิ่ม method ให้กับ NSString เพื่อใช้ตรวจสอบว่า string ที่กำหลดให้ขณะนั้นเป็น URL หรือไม่ หน้าตาของมัน จะประมาณนี้ครับ

#import 

@interface NSString (Utilities)
-(BOOL) isURL;
@end

จะเห็นว่าหน้าตาเหมือนการประกาศ class จะต้องบอกสักนิสว่า method นี้ใช้แสดงให้เห็นการทำ category เท่านั้นให้ดูเท่านนั้นไม่ใช้ method สำหรับการตรวจสอบ URL ที่ดีเลยไม่ควรเอาไปใช้จิงนะครับ

ครับมาดูว่าเราจะ implement อย่างไร

#import “NSString-Utilities.h”

@implementation NSString (Utilitites)

-(BOOL) isURL
{
	if([Self hasPrefix:@”http://”])
		return YES;
	else
		return NO;
}

@end

ครับตอนนี้เรามาลองดูการใช้งานว่าเป็นอย่างไร

NSString* string1 = @”http://pixar.com/”;
NSString* string2 = @”Pixar”;

if([string1 isURL])
	NSLog(@”string1 is a URL”);

if([string2 isURL])
	NSLog(@”string2 is a URL”);

ต่างกับ subclass นะครับ category ไม่สามารถ เพิ่ม instance variable ได้ อีกอย่างหนึ่งที่สามารถทำได้คือเราสามารถใช้ category ในการ override method ที่มีอยู่ได้ แต่ต้องทำอย่างระมัดระวังอย่างมากมาย

จะบอกว่าเนื้อหาในเรื่อง Obective-c Mynote จบลงตรงนี่ครับ เรื่องที่กล่าวมาทั้งหมดนั้นอ้างอิง หรือ แปลจาก http://www.cocoadevcentral.com เพื่อน ๆ สามารถหาอ่านเพิ่มเติมได้ที่นี่และ ค้นหาได้ใน internet

ในเรื่องของการเขียนโปรแกรมกับ Objective-c นั้นส่วนต้วผมเป็นเรื่อสนุกและท้าทายนะครัย ที่กล่าวมาทั้งหมดนั้นเป็นพื้นฐานของการเขียนObjective-c เรายังต้องเรียนรู้เพิ่มอีกในเรื่องของ cocoa (เขาออกเสียงว่า โกโก้ หรือปล่าวไม่รู้นะ) และ cocoa graphic
Cocoa นั้นเป็นกลุ่มของ Framework ซึ่งประกอบด้วย Foundation, AppKit และ CoreData เป็นต้น จะช่วยให้เราพัฒนา application ให้กับ MAC OS X ซึ่งเป็นที่ทราบกันว่าเทคโนโลยีบางส่วน นั้นสืบทอดมากจาก NeXTSTEP operating system ครับค่อยพูดกันในรายละเอียดใน บทความ ต่อ ๆอีกก็แล้วกันนะครับ

ธีระพงษ์ สนธยามาลย์
s.teerapong@gmail.com

Categories: Objective-C Tags: ,

Objective-C Note ภาคต่อ 2

ยังไม่หมดนะครับเรายังมีหัวข้อที่หน้าสนใจและควรรู้เป็นพื้นฐานต่ออีก สำหรับผู้ที่สนใจใฝ่เรียนรู้หรือต้องการนำไปใช้จริงจังกับการพัฒนางาน โปรแกรมใช้บน MAC OS หรือ IOS  ก็ตามนะครับ แล้วแต่ความสะดวก

การจัดการ Memory Management  เพิ่มเติมจากเดิม
การจัดการ memory ของ Objective-C เรียกว่า reference counting สิ่งที่เราต้องทำก็คือติดตาม reference ของเรา และในระหว่าง runtime จะต้องมีการทำ free memory

พูดให้ง่ายก็คือ เราทำการจอง (alloc) memory ให้ object และเก็บค่าไว้ระหว่างใช้งาน (retain) แล้วทำการ คืนกลับ (release) หลังจากเลิกใช้แล้ว ให้ทำทุกการทำ alloc และ retain
หมายความว่า หาก เราทำการ alloc ครั้งหนึ่ง retain ครั้งหนึ่ง เราจะต้องทำการ release ถึง 2 ครั้งตามภาพ

test

ภาพจาก http://www.cocoadevcentral.com/

ที่กล่าวมาเป็นทฤษฎีของ reference counting ในทางปฏิบัติ เรามีเหตุผลในการสร้าง object อยู่ 2 เหตุผลก็คือ
1. เพื่อใช้งานเป็น instance variable
2. ใช้งานภายใน function หรือภายใน method ของ object

โดยมากแล้ว setter ของ instance variable ควรจะทำแค่การ กำจัดค่าเก่า แบบ autorelease และ retain ค่าใหม่ เพื่อความมั่นใจเราควรที่จะทำการ release มันใน dealloc อีกที ด้วย

งานที่ต้องทำจริง ๆ คือการจัดการกับ local references ใน method หรือ function และก็มีกฏเพียงข้อเดียวเท่านั้นก็คือ ถ้าหหากเราสร้าง object ด้วย alloc หรือ copy ให้ทำ release หรือ autorelease ที่ท้าย function แต่ถ้าหากเราสรา้ง object ด้วยวิธีอื่น ๆ ที่ไม่ได้พูดถึง ไม่ต้องทำอะไร

ตัวอย่างนี้ สำหรับกรณีในข้อ 1 (instance variable )

-(void) setTotalAmount:(NSNumber *)input
{
	[totalAmount autorelease];
	totalAmount = [input retain];
}

-(void) dealloc
{
	[totalAmount release];
	[super dealloc];
}

จะเห็นว่าเรามี totalAmount เป็น instance variable และมีการ กำหนดค่าด้วยการ retain เราทำการ release มันอีกครั้ง ใน dealloc ถึงแม้เราจะกำหนด autorelease ให้มันก็ตาม

ตัวอย่างต่อไปก็คือกรณีถัดมาในข้อ 2 การจัดการกับ local reference สิ่งเดียวที่เราต้องทำก็คือ release object ที่ถูกสร้างด้วย alloc

NSNumber* value1 = [[NSNumber alloc] initWithFloat:8.75];
NSNumber*value2 = [NSNumber numberWithFloat:14.78];
 
// only  release value1 , not value2

และตัวอย่างนี้ แสดงให้เห็นการใช้ local reference กำหนดค่าให้กับ instance variable ของ object

NSNumber* value1 = [[NSNumber alloc] initWithFloat:875];
[self setTotal:value1];

NSNumber*value2 =[NSNumber numberWithFloat:14.78];
[self setTotal:value2];

[value1 release];

สังเกตุวิธีการใช้กฏ สำหรับการ จัดการกับ local reference นั้นเหมือนกัน ไม่ต้องใส่ใจว่าเราใช้มันกำหนดค่าให้กับ instance variable หรือไม่ และเราก็ไม่ต้องสนใจว่า setter นั้นมีหน้าตาเป็นอย่างไร

ถ้าเราเข้าใจจุดนี้เราก็จะเข้าใจ 90% ของสิ่งที่ควรรู้ เกี่ยวกับการบริหารจัดการ memory ใน Objective-C จบปะ

Properties
อย่างที่เราได้ทำการ เขียน accessor method ให้กับ caption และ author ก่อนหน้านั้น เราอาจจะสังเกตุได้ว่า code นั้นค่อนข้างตรงไปตรงมาน่าจะสามารถทำให้เป็นรูปแบบที่ generalized ได้
Properties คือคุณลักษณะที่มีอยู่ใน objective-C ที่จะทำให้เราสามารถสร้าง accessor ได้อัติโนมัติ และนอกจากนี้ยังมีประโยชน์ข้างเคียงอืนๆอีก เรารองมาแปลง Photo class ให้ใช้ properties กันดู

ก่อนหน้ามีหน้าตาแบบนี้

#import 
@interface Photo : NSObject {
	NSString* caption;
	NSString* photographer;
}

-(NSString*) caption;
-(NSString*) photographer;

-(void) setCaption:(NSString*) input;
-(void) setPhotographer:(NSString*)input;

@end

ซึ่งเมื่อแปลงแล้วจะได้หน้าตาแบบนี้

#import
@interface Photo : NSObject {
	NSString* caption;
	NSString* photographer;
}
@property (retain) NSString* caption;
@property (retain) NSString* photographer;

@property คือ directive สำหรับการประกาศ property
“retain” ในเครื่องหมายวงเล็บเป็นการกำหนดว่า setter ต้องเก็บค่า input ไว้ และส่วนที่เหลือของบรรทัดนั้นเป็นการกำหนด ชนิด (type) และ ชื่อของ property;

ต่อไป ลองมาดูว่าหากใช้ @property จะต้องimplement class อย่าไร

#import “Photo.h”

@implementation Photo 

@syncthesize caption;
@syncthesize phtographer;

-(void) dealloc
{
	;
	[photographer release];
	[super dealloc];
}

@end

@synthesize directive จะทำหน้าที่ในการ สร้าง setter และ getter ให้อัตโนมัติ ดังนั้นสิ่งที่เราต้องทำให้ class นี้แค่การ implement dealloc method

สิ่งที่ควรทราบก็คือ accessor จะถูกสร้างขึ้นในกรณีที่มันไม่มีเท่านั้นเราสามารถที่จะ กำหนด @synthesize ให้กับ propertiy ไว้พร้อมกับการ implement setter และ getter ของเราเองได้ ถ้าเราต้องการ หน้าที่ของ compiler คือการเติม ในส่วนที่เราขาดไป ครับ (เป็นทั้งข้อดี และ ข้อเสียนะเนี่ยย)
อย่างไรก็ตาม เรื่องของ property นี้มีรายละเอียดเพิ่มเติมอีกมากครับ ควรจะต้องศษกาาเพิ่มเติมเพื่อการใช้งานที่ดีนะครับ

ถ้าจะให้ดีก็ ว่ากันต่อไปใน คราวหน้านะครับ
ธีระพงษ์ สนธยามาลย์
s.teerapong@gmail.com

Categories: Objective-C Tags:

[Objective-C myNote] ภาคต่อ 1

หายไปนานแสนนานไม่ได้เข้ามา update เลย นานที่ว่าหน่วยไม่ใช่เป็นเดือนนะครับ หน่วยเป็นปี ฮะ ฮะ สำหรับ Note ที่ทิ้งค้างไว้ ก็จะมาต่อกันให้จบในคราวนี้แหละครับ สำหรับคราวที่แล้วนั้นมาจบตรงที่  การออกแบบ class interface – Designing a Class interface 

เริ่มที่ หัวข้อนี้ใหม่เลย ครับ

การออกแบบ class interface – Designing a Class interface

การสร้าง class มี 2 ส่วนคือ

  • Class interface  เก็บไว้ใน  ClassName.h
    • Define instance variable and public methods
  • Implementation เก็บไว้ใน ClassName.m
    • Actual code
    • Private method ซึ่ง ใช้ภายใน
ตัวอย่าง class ชื่อ photo
#import <Cocoa/Cocoa.h>
@interface Photo : NSObject {
     NSString* caption;
     NSString* photographer;
 }
 @end

–    ใช้ #import directive เพื่อบอก compiler ว่าจะเรียกใช้ library อะไร
–    @interface บอกว่าเป็นการ ประกาศ class ชื่อ photo   colon บอกว่า photo มี superclass เป็นอะไร
–    ใน curly bracket ประกาศ variable 2 ตัว ชื่อ caption, photographer ซึ่งเป็น NSString
–    จบด้วย @end

ลองมาเพิ่ม  getter ให้กับ instance variable กันครับ

#import <Cocoa/Cocoa.h>
@interface Photo : NSObject {
  NSString* caption;
  NSString* photographer;
}
  caption;
  photographer;
@end;

จำได้ไหม ครับว่าโดยการปฏิบัติทั่วไปแล้ว method ของ Objective-C นั้นจะไม่ใส่ส่วนนำ หรือ prefix “get” single dash “-” นำหน้าชื่อของ method นั้นหมายถึง ชื่อนั้นเป็น instance method ส่วนเครื่องหมาย “+” หรือ plus นำหน้่า method ก็หมยถึง ชื่อนั้นเป็น class method
lass method หมายถึง class ที่สามารถถูกเรียกใช้ได้ในขณะที่ไม่ได้เป็น object หรือ class method ก็คือ static method
สมมุติ เรามี class ชื่อว่า MathFunc ดังนี้

@interface MathFunc : Nsobject
{
}
 +(int) square:(int)num;
@end

@implementation MathFunc
 +(int) square: (int)num
 {
	return num*num;
 }
@end

ดังนั้นการเรียกใช้จะเป็นดังตัวอย่างครับ โดยที่เราไม่ต้องมีการ instantiate ให้เป็น object เราก็สามารถเรียกใช้ฟังก์ชันได้ ( นี่เป็นการแสดงความหมายและการใช้งาน class method )

   value = [[square alloc]   num:5] ;

โดย default แล้ว compiler จะกำหนดชนิดของค่าที่ส่งกลับหรือค่าที่ return ของ method ใน Objective-C ให้เป็น ชนิด id จาก code ข้างบนการประกาศ method แบบนั้นในทางเทคนิคแล้วถูกต้องไม่ผิดอะไรแต่มันดูไม่ค่อยปกติ เราควรจะกำหนดชนิดของค่า return ให้เจาะจงไปเลยว่าเป็นชนิดใดดังนี้

#import 
@interface Photo : NSObject{
	NSString* caption;
	NSString* photographer;
}
-(NSString*) caption;
-(NSString*) photographer;
@end;

ลองมาเพิ่ม getter ให้กับ instance variable กันครับ

#import 
@interface Photo : NSObject{
	NSString * caption;
	NSString * photographer;
}
-(NSString*) caption;
-(NSString*) photographer;

-(void) setCaption: (NSString*)input;
-(void) setPhotographer:(NSString*)input;

@end

setter ไม่มีการ return ค่า เราจึงกำหนดด้วย void เหมือนภาษา C

การ Implement class (Class implementation)
มาทำการ Implementation โดยเริ่มที่ getter กันครับ

#import “Photo.h”

@implementation Photo 
-(NSString*) caption{
	return caption;
}

-(NSString*) photographer{
	return photographer;
}
@end 

ในส่วนของการ implementation นั้น กำหนดด้วย @implementation ตามด้วยชื่อ class ลงท้ายด้วย @end ลักษณะการเขียนเหมือนกับ interface method ที่ต้องการ implement ทั้งหมดจะต้องอยู่ระหว่าง 2 statement นี้นะครับ ส่วนของ getter นั้น ก็จะค่อนข้างคุ้น และง่ายต่อการเขียน
ต่อไปดูที่ setter ซึ่งเป็นส่วนที่จะต้องอธิบายเพิ่มเติมจาก code ที่แสดงให้ดู ดังนี้

-(void) setCaption: (NSString*)input
{
	[ccaption autorelease];
	caption = [input retain];
} 

-(void) setPhotographer:(NSString*) input
{
	[photographer autorelease];
	photographer = [input retain];
}

setter แต่ละตัวจะเห็นว่าต้องเกี่ยวข้องกับตัวแปร สองตัว ตัวแรกคือ ตัวที่อ้างอิงไปที่ ตัวแปรของ object เอง เช่น caption ส่วนตัวแปรที่ สองเป็น input ใน environment ที่เป็น garbage collected เราสามารถกำหนดค่าใหม่ได้โดยตรง เช่น

-(void) setCaption:(NSString*) input{
	caption = input;
}

แต่ถ้่หากเราไม่สามารถใช้ garbage collection ได้เราต้องทำการ release ค่าเก่า แล้วกำหนดค่าใหม่ให้ด้วย เครื่องหมาย autorelease และ retain ตามตัวอย่างครับ
การ ปล่อย หรือ free ข้อมูลการอ้างอิง (reference) ไปยัง object นั้นมี 2 วิธีคือ release และ autorelease
สำหรับ release นั้นจะทำการกำจัด reference ทันที ส่วน autorelease นั้นจะทำการกำจัดหลังจาก ระยะเวลาหนึ่ง และจะต้องหลังจากที่ function นั้นจบลงแล้วด้วย นอกจากจะมีการเปลี่ยนแปลงเพิ่มเติม
** autorelease method ใช้กับ setter ดูจะปลอดภัยกว่าเพราะว่าตัวแปรสำหรับค่าเก่าและค่าใหม่นั้นสามารถชี้ไปยัง object เดียวกันได้ หรือ ที่ตำแหน่งเดียวกันเราไม่จำเป็นต้องทำการ release object ที่เราต้องการ retain ค่า ทันทีทันใดก็ได้
ครับดูจะสบสน ในตอนนี้ แต่หลังจากผ่านไปสักระยะหนึ่งก็จะพอเข้าใจขึ้นนะครับ เราไม่จำเป็นต้องเข้าใจทุกสิ่งอย่างทั้งหมดในตอนนี้

Init
เราสามารถ ใช้ Init method สำหรับการกำหนดค่า เริ่มต้นให้กับตัวแปร instance ได้

-(id) init 
{
	if( self = [super init])
	{
		[self setCaption:@”Default caption”];
		[self setPhotographer:@”Default Photographer”];
	}
	return self;
}

code ค่อนข้างจะอธิบายการทำงานได้ดีอยู่แล้ว ยกเว้นในส่วนของ if( self=[super init]) ซึ่งมีลักษณะการใช้เหมือนการ assign ค่า [super init] ให้กับ self ในความรู้สึกมันแปลก ๆ
อธิบายได้ว่า เป็นการร้องขอให้ superclass ทำการ initialization ตัวเอง ส่วน if statement นั้นจะตรวจสอบว่าการ initialization นั้นสำเร็จหรือไม่ก่อนการ กำหนดค่าให้กับ self

Dealloc
Dealloc method นี้จะถูกเรียกเมื่อ object กำลังถูก remove จาก memory ซึ่งเป็นการดีที่จะทำการปล่อย reference ต่าง ๆ ที่อ้าอิงอยู่ออกไปด้วย

-(void) dealloc
{
	;
	[photographer release];
	[super dealloc];
}

สองบรรทัดแรกนั้นเป็นการปล่อยหน่วยความจำในส่วนของ instance variable เราไม่ใช้การ autorelease ใช้ release จะเร็วกว่า

บบรทัดสุดท้ายนั้นสำคัญมาก เราจะต้องทำการ ร้องขอไปยัง superclass ให้ทำการปล่อยหน่วยความจำในส่วนของตัวเองด้วย หากเราไม่ทำขั้นตอนนี้ object จะไม่ถูกกำจัด ทำให้เกิด memory leak.

Dealloc method นั้นจะไม่ถูกเรียกให้ทำงานหาก garbage collection ทำงาน เราจะทำ finalizeแทน ครับในส่วนของการจัดการ memory นั้นจะขอเพิ่มเติมในรายละเอียดภายหลัง

การบริหารจัดการกับ memory เบื้องต้น [ Basic Memory Management ]
ถ้าหากเรากำลังพัฒนา application บน MAC OS X เราสามารถที่จะเลือกให้ garbage collection ทำงานได้ หมายความว่า จะทำให้เราไม่ต้องมาคำนึงถึงเรื่องการจัดการ memory จนกว่าเราจะเข้าสู่การเขียนแบบที่ซับซ้อนขึ้น เรามาเรียนรู้กันสักนิดหนึ่งนะครับ
ตั้งแต่นี้ไป สำคัญนะครับ

หากเราสร้าง object แบบใช้ alloc เราต้องทำการ release object นั้นหลังจากที่ใช้งานเสร็จแล้ว และสิ่งที่สำคัณอีกอย่างหนึ่งก็คือจะต้องไม่ ไป release object ที่ ถูกกำหนดเป็น autorelease object เพราะว่าจะทำให้ application นั้นล่มเอาง่าย ๆ

// string1 will be release automatically
NSString * string1 = [NSString string];

// must release this when done
NSString * string2 = [[NSString alloc] init];
[string2 release]

สำหรับ tutorial นี้ให้จำไว้ว่า object แบบ autorelease นั้นจะถูกกำจัดเมื่อจบ funciton นั้น

มีหลายสิ่งที่เราต้องเรียนรู้เกี่ยวกับการจัดการ memory ซึ่ง ความเข้าใจจะมากขึ้นเมื่อเราได้เรียนรู้หรือเข้าใจ concept เพิ่มเติมจากนี้

จบลงตรงนี้ก่อนนะครับ ค่อยต่อกันตอนต่อไปนะครับ
s_teerapong2000@yahoo.com

Categories: Objective-C