main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
int wheels = 4;
int seats = 2;
NSLog(@"wheels : %i, seats : %i", wheels, seats);
// wheels : 4, seats : 2
}
return 0;
}
@autoreleasepool은 메모리 할당과 해제를 자동으로 처리주는 영역이고, 해당 코드블럭 안에 코드를 작성하면 됩니다.
위의 프로그램을 객체지향 형식으로 바꿔 보면
// 정의부
@interface Vehicle : NSObject
@end
// 구현부
@implementation Vehicle
@end
int main(int argc, const char * argv[]) {
}
Vehicle 클래스를 만들고, 콜론(:) 뒤에는 이 클래스가 상속받고 있는 슈퍼클래스를 작성합니다. 작성하지 않는다면 컴파일러가 자동으로 최상위클래스인 NSObject를 할당합니다. 클래스 파일을 따로 만들어도 되고, 이렇게 main 파일에 같이 작성해도 되는데 그럴 경우 정의부와 구현부는 main 위에 위치해야 합니다.
@interface Vehicle : NSObject {
// member variable
int wheels;
int seats;
}
// member method
-(void)setWheels:(int)w;
-(void)setSeats:(int)s;
-(void)print;
@end
정의부에 member variable과 member method는 이렇게 작성하면 됩니다. 단, variable의 경우 정의부에서 초기화를 하면 안 됩니다.
method의 경우 +로 시작하면 클래스 메소드, -로 시작하면 인스턴스 메소드입니다. 클래스 메소드는 동적할당을 하지 않고도 사용할 수 있고, 인스턴스 메소드는 동적할당을 거친 후에 사용할 수 있습니다.(void)에는 리턴타입이 들어가고, 함수명 뒤에는 콜론(:)과 인자들이 작성됩니다.
@implementation Vehicle
-(void)setWheels:(int)w{
wheels = w;
}
-(void)setSeats:(int)s{
seats = s;
}
-(void)print{
NSLog(@"wheels: %i, seats: %i", wheels, seats);
}
@end
정의부에 작성했던 setter 메소드들을 구현합니다.
인스턴스를 만들어서 메소드들이 잘 동작하는지 확인해보겠습니다.
int main(int argc, char * argv[]) {
@autoreleasepool {
Vehicle *vehicle = [[Vehicle alloc] init]; // create instance object
[vehicle setWheels:4];
[vehicle setSeats:2];
[vehicle print];
}
return 0;
}
Vehicle *vehicle = [Vehicle new];
로도 Vehicle의 인스턴스를 생성할 수 있습니다. 하지만 Objective-C에서는 관례적으로 new를 쓰지 않고, 동적할당(alloc)과 초기화(init)를 따로따로 합니다. Vehicle *vehicle = [[Vehicle alloc] init];
처럼 alloc한 결과물을 가지고 init할 수 있도록 메소드체인으로 연결할 수 있기 때문입니다. 이 메소드체인은 Objective-C에서 상당히 많이 쓰이는 방식입니다. 또한 클래스이므로 포인터를 잊지 않아야 합니다.
[vehicle setWheels:4];
로 메소드를 호출합니다.
Objective-C는 다른 언어들과 달리 [Receiver Message]; 형식을 사용합니다. 리시버에게 메시지를 보낸다는 의미인데, [vehicle setWheels:4];라 하면 Vehicle에게 setWheels 메시지를 보내는 것입니다.
Objective-C에서 getter는 주로 변수명과 메소드명을 일치시켜 사용합니다.
@interface Vehicle : NSObject {
int wheels;
int seats;
}
-(void)setWheels:(int)w; // setter
-(void)setSeats:(int)s; // setter
-(int)wheels; // getter
-(int)seats; // getter
@end
@implementation Vehicle
-(void)setWheels:(int)w{
wheels = w;
}
-(void)setSeats:(int)s{
seats = s;
}
-(int)wheels{
return wheels;
}
-(int)seats{
return seats;
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
Vehicle *vehicle = [[Vehicle alloc] init]; // create instance object
[vehicle setWheels:4];
[vehicle setSeats:2];
NSLog(@"wheels : %i, seats : %i", [vehicle wheels], [vehicle seats]);
}
return 0;
}
정의부에 property를 사용하면 setter, getter가 자동으로 만들어집니다. setter는 set변수명(ex. setWheels), getter는 변수명(ex. wheels)으로 명명됩니다.
property를 사용할 때 구현부에는 synthesize를 사용하는 것이 좋습니다. 사용하지 않아도 작동은 하지만 불편한 점이 있기 때문입니다. << 나중에 ~
property를 사용하면 또한 멤버변수를 선언하지 않아도 자동으로 멤버변수까지 선언이 됩니다.
@interface Vehicle : NSObject {
int wheels; // 이 부분 생략 가능
int seats; // 이 부분 생략 가능
}
@property int wheels;
@property int seats;
@end
@implementation Vehicle
@synthesize wheels;
@synthesize seats;
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
Vehicle *vehicle = [[Vehicle alloc] init]; // create instance object
[vehicle setWheels:4];
[vehicle setSeats:2];
NSLog(@"wheels : %i, seats : %i", [vehicle wheels], [vehicle seats]);
}
return 0;
}
property에 옵션을 주면 getter를 재정의할 수 있습니다.
@property (getter=getWheels) int wheels;
이렇게 작성하면 기존의 변수명과 동일하게 명명되던 wheels라는 이름의 getter는 자동으로 생성되지 않고, getWheels라는 이름의 getter가 생성됩니다.
setter도 재정의할 수 있습니다.
@property (getter=getWheels, setter=setUpWheels:) int wheels
setWheels가 아닌 setUpWheels가 생성됩니다. setter는 무조건 인자를 적어도 하나는 받아야 하기 때문에 콜론(:)을 찍어줘야 합니다.
setter
[vehicle setWheels:4];
[vehicle setSeats:2];
또는
vehicle.wheels = 4;
vehicle.seats = 2;
getter
[vehicle wheels]
[vehicle seats]
또는
vehicle.wheels
vehicle.seats
상황과 편의에 따라 괄호를 사용해도 되고 dot를 사용해도 됩니다. 메소드 체인을 사용해서 한 줄에 코드를 작성하고자 할 때는 주로 괄호를 사용합니다.
[Ctrl + N] > Cocoa Touch Class로 클래스파일을 생성합니다. 짠 헤더파일과 구현파일이 생겼습니다. main에 있던 정의부와 구현부를 각각 Vehicle.h와 Vehicle.m으로 옮겨줍니다.
Vehicle.h
#import <Foundation/Foundation.h>
@interface Vehicle : NSObject {
// member variable
int wheels;
int seats;
}
// member method
-(void)setWheels:(int)w;
-(void)setSeats:(int)s;
-(void)print;
@end
Vehicle.m
#import "Vehicle.h"
@implementation Vehicle
-(void)setWheels:(int)w{
wheels = w;
}
-(void)setSeats:(int)s{
seats = s;
}
-(void)print{
NSLog(@"wheels: %i, seats: %i", wheels, seats);
}
@end
main.m
#import <UIKit/UIKit.h>
#import "Vehicle.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
Vehicle *vehicle = [[Vehicle alloc] init]; // create instance object
[vehicle setWheels:4];
[vehicle setSeats:2];
[vehicle print];
}
return 0;
}
Apple에서 제공하는 라이브러리는 꺾쇠(<>)로 import, 직접 만든 것들은 쌍따옴표("")로 import합니다. 꺾쇠냐 쌍따옴표나에 따라 컴파일할 때 찾는 위치가 달라지기 때문입니다.
wheels와 seats를 동시에 초기화하는 메소드를 만들어보겠습니다.
-(void)setVehicle:(int)w Seats:(int)s;
-(void)setVehicle:(int)w Seats:(int)s{
wheels = w;
seats = s;
}
int main(int argc, char * argv[]) {
@autoreleasepool {
[vehicle setVehicle:4 Seats:2];
}
return 0;
}
파라미터를 콜론(:) 기준으로 인식하므로 이런 식으로 작성해주면 됩니다.
int main(int argc, char * argv[]) {
@autoreleasepool {
// 1
NSString *str = [[NSString alloc] init];
str = @"This is NSString";
NSLog(@"str : %@", str);
// 2
NSString *str2 = [[NSString alloc]initWithString:@"This is NSString2"];
NSLog(@"str2 : %@", str2);
// 3
NSString *str3 = @"This is NSString3";
NSLog(@"str3 : %@", str3);
}
return 0;
}
NSString은 초기화 방법이 여러개입니다.
NSString은 immutable class라서 값을 통째로 재할당할 순 있는데 부분을 수정할 수는 없습니다. (부분을 수정할 수 있는 것도 있는데, NSMutableString입니다.)
따라서 부분문자열을 얻기 위해서는 아래와 같이 메소드를 사용합니다.
int main(int argc, char * argv[]) {
@autoreleasepool {
NSString *str = @"This is NSString";
NSString *result;
NSString *result2;
NSString *result3;
NSString *result4;
result = [str substringFromIndex:6];
result2 = [str substringToIndex:3];
result3 = [[str substringToIndex:11]substringFromIndex:8];
result4 = [[str substringWithRange:NSMakeRange(8, 3)]lowercaseString];
NSLog(@"result : %@", result); // result : s NSString
NSLog(@"result2 : %@", result2); // result2 : Thi
NSLog(@"result3 : %@", result3); // result3 : NSS
NSLog(@"result4 : %@", result4); // result4 : nss
}
return 0;
}