Objective-C (클래스)

이한솔·2024년 8월 1일

Objective-C 문법 🍎

목록 보기
8/12

클래스

클래스는 객체 지향 프로그래밍에서 중요한 개념 중 하나로, 데이터와 그 데이터를 조작하는 메서드를 하나의 단위로 캡슐화하는 구조다.
클래스는 객체를 생성하기 위한 설계도 또는 템플릿 역할을 하며, Objective-C에서 모든 클래스는 NSObject 라는 기본 클래스에서 파생된다.
헤더 파일(.h)에서는 클래스의 인터페이스를 선언하고, 구현 파일(.m)에서는 실제 기능을 구현한다.

// 선언부
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

- (void)greet;
- (void)celebrateBirthday;

@end


// 구현부
#import "Person.h"

@implementation Person

- (void)greet {
    NSLog(@"Hello, my name is %@", self.name);
}

- (void)celebrateBirthday {
    self.age += 1;
    NSLog(@"Happy Birthday %@! You are now %ld years old.", self.name, (long)self.age);
}

@end


// 사용부
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        person.name = @"John";
        person.age = 30;
        
        [person greet];
        [person celebrateBirthday];
    }
    return 0;
}


초기화

객체 지향 프로그래밍에서 초기화는 객체를 생성하고 그 객체의 상태를 설정하는 과정으로 이 과정은 객체가 생성될 때 일관된 상태로 시작되도록 보장하며, 객체의 멤버 변수나 프로퍼티에 초기값을 설정한다.

기본 초기화 메서드 (-init)

// 선언부 
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

// 기본 초기화 메서드
- (instancetype)init;

// 메서드 선언
- (void)greet;

@end


// 구현부
#import "Person.h"

@implementation Person

// 기본 초기화 메서드
- (instancetype)init {
    self = [super init];
    if (self) {
        _name = @"Unknown"; // 기본값 설정
        _age = 0;           // 기본값 설정
    }
    return self;
}

// 메서드 구현
- (void)greet {
    NSLog(@"Hello, my name is %@ and I am %ld years old.", self.name, (long)self.age);
}

@end

// 사용부
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 기본 초기화 메서드를 사용하여 객체 생성
        Person *person = [[Person alloc] init];
        [person greet]; // 출력: Hello, my name is Unknown and I am 0 years old.
    }
    return 0;
}

커스텀 초기화 메서드

커스텀 초기화는 객체를 생성 시 프로퍼티를 직접 설정할 수 있다.

// 선언부
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

// 커스텀 초기화 메서드
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;

// 메서드 선언
- (void)greet;

@end


// 구현부
#import "Person.h"

@implementation Person

// 커스텀 초기화 메서드
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = name ?: @"Unknown"; // nil 대체값 설정
        _age = age;
    }
    return self;
}

// 메서드 구현
- (void)greet {
    NSLog(@"Hello, my name is %@ and I am %ld years old.", self.name, (long)self.age);
}

@end


// 사용부
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 커스텀 초기화 메서드를 사용하여 객체 생성
        Person *person = [[Person alloc] initWithName:@"John" age:30];
        [person greet]; // 출력: Hello, my name is John and I am 30 years old.
    }
    return 0;
}


멤버변수와 프로퍼티

프로퍼티: 객체의 상태를 외부에서 접근하고 수정할 수 있는 인터페이스를 제공하는데 사용된다. @property 선언을 통해 정의한다. 접근자 메서드(getter)와 설정자 메서드(setter)를 자동으로 생성하여, 클래스의 데이터에 대한 직접 접근을 제어한다.
@synthesize는 Objective-C에서 프로퍼티의 자동 생성된 인스턴스 변수를 정의하고, 기본 getter 및 setter 메서드를 구현하는 데 사용된다. 현재는 Xcode의 컴파일러가 자동으로 인스턴스 변수를 생성하고 기본 getter/setter 메서드를 제공하기 때문에, 코드에서 생략 가능하다.

멤버 변수: 클래스의 인스턴스가 가지는 데이터 저장소,
클래스 내부에서 데이터 저장과 관리에 사용된다. 일반적으로 클래스의 구현 파일(.m)에서 정의하고 @private, @protected 접근 제어자를 사용하여 외부에서 직접 접근할 수 없으며, 클래스 내부의 메서드를 통해 관리된다.

// 선언부 
#import <Foundation/Foundation.h>

@interface Person : NSObject

// 프로퍼티 선언
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

// 퍼블릭 메서드 선언
- (void)publicMethod;

@end


// 구현부
#import "Person.h"

@implementation Person {
    // 멤버 변수 정의
    NSString *_internalIdentifier;
    NSInteger _internalCount;
}

// 기본 초기화 메서드
- (instancetype)init {
    self = [super init];
    if (self) {
        _name = @"Unknown";
        _age = 0;
        _internalIdentifier = @"DefaultID";
        _internalCount = 0;
    }
    return self;
}

// 퍼블릭 메서드 구현
- (void)publicMethod {
    NSLog(@"Name: %@, Age: %ld", self.name, (long)self.age);
}

@end


// 사용부
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        person.name = @"John";
        person.age = 30;
        
        NSLog(@"Name: %@", person.name);
        NSLog(@"Age: %ld", (long)person.age);
        
        [person publicMethod];
    }
    return 0;
}

프로퍼티 키워드

readonly: 프로퍼티에 대한 읽기 전용 접근을 허용
readwrite: 프로퍼티에 대한 읽기와 쓰기 모두를 허용
atomic: 멀티 스레드 환경에서 동시에 프로퍼티에 접근하려 할 때, 자동으로 이 프로퍼티에 Lock 기능을 제공한다. 모든 프로퍼티에 사용 시 성능이 저하될 수 있어서 꼭 필요한 경우가 아니면 nonatomic을 사용한다.

이 외에도 메모리 관리 키워드가 있다.
ARC/MRC

@property (nonatomic, strong) NSString *name;
@property (nonatomic, weak) id<SomeDelegate> delegate;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, strong, readwrite) NSString *name;
@property (atomic, strong) NSString *name;


메소드

객체의 상태를 조작하거나 객체의 동작을 정의하는 데 사용된다.

인스턴스 메서드: - 기호로 선언하고, 특정 객체 인스턴스에서 호출되며, 객체의 상태를 조작하거나 객체에 대한 작업을 수행한다.

타입 메서드: +기호로 선언하고, 클래스 자체에서 호출되며, 인스턴스화 없이 클래스 이름으로 호출해서 클래스의 상태를 조작하거나 클래스에 관련된 작업을 수행한다.

// 선언부
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

// 인스턴스 메서드 선언
- (void)greet; 

// 클래스 메서드 선언
+ (Person *)personWithName:(NSString *)name age:(NSInteger)age;

@end


// 구현부
#import "Person.h"

@implementation Person

// 인스턴스 메서드 구현
- (void)greet {
    NSLog(@"Hello, my name is %@ and I am %ld years old.", self.name, (long)self.age);
}

// 클래스 메서드 구현
+ (Person *)personWithName:(NSString *)name age:(NSInteger)age {
    Person *newPerson = [[Person alloc] init];
    newPerson.name = name;
    newPerson.age = age;
    return newPerson;
}

@end


// 사용부
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 클래스 메서드 사용하여 Person 객체 생성
        Person *person = [Person personWithName:@"John" age:30];
        
        // 인스턴스 메서드 사용
        [person greet]; // 출력: Hello, my name is John and I am 30 years old.
    }
    return 0;
}


상속

클래스 간에 속성과 메서드를 공유할 수 있어 코드의 재사용성과 유지보수성을 높일 수 있다. 하나의 부모 클래스를 상속받을 수 있고, 자식 클래스는 부모 클래스의 속성과 메서드를 상속받아 사용할 수 있다. 자식클래스에서 부모클래스의 private 접근제어자와 구현부에는 접근 불가능하고, 메소드는 오버라이딩 가능하다. 부모 클래스의 메서드를 호출할 때는 super 키워드를 사용한다.

// Animal 클래스 선언부
#import <Foundation/Foundation.h>

@interface Animal : NSObject

@property (nonatomic, strong) NSString *name;

- (instancetype)initWithName:(NSString *)name;
- (void)makeSound;

@end


// Animal 클래스 구현부
#import "Animal.h"

@implementation Animal

- (instancetype)initWithName:(NSString *)name {
    self = [super init];
    if (self) {
        _name = name;
    }
    return self;
}

- (void)makeSound {
    NSLog(@"Some generic animal sound");
}

@end


// Dog 클래스 선언부 
#import "Animal.h"

@interface Dog : Animal

- (void)fetch;

@end


// Dog 클래스 구현부
#import "Dog.h"

@implementation Dog

// swift와 다르게 override 키워드를 붙이지않고, 같은 이름으로 메소드를 정의하면 자동으로 오버라이딩된다.
- (void)makeSound {
    NSLog(@"Woof! Woof!");
}

- (void)fetch {
    NSLog(@"%@ is fetching!", self.name);
}

@end


// 사용부
#import <Foundation/Foundation.h>
#import "Dog.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Dog *myDog = [[Dog alloc] initWithName:@"Buddy"];
        [myDog makeSound];  // 출력: Woof! Woof!
        [myDog fetch];      // 출력: Buddy is fetching!
    }
    return 0;
}


확장

Category

카테고리를 사용하면 기존 클래스에 새로운 메서드를 추가하거나 메서드를 분리하여 코드의 조직화와 관리가 용이해진다. 프로퍼티 추가나, 기존 메서드를 오버라이드는 불가능하고 새로운 메서드를 추가하거나 기존 메서드의 동작을 확장하는 데 사용된다. 카테고리에 선언한 메서드는 원래 클래스의 인스턴스와 모든 하위 클래스에서 사용할 수 있다.

// 카테고리 선언부
#import <Foundation/Foundation.h>

@interface NSString (MyAdditions)

+ (NSString *)getString;

@end

// 카테고리 구현부
#import "NSString+MyAdditions.h"

@implementation NSString (MyAdditions)

+ (NSString *)getString {
    return @"문자열 카테고리 추가";
}

@end

// 사용부
#import <Foundation/Foundation.h>
#import "NSString+MyAdditions.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 카테고리를 사용하여 추가된 클래스 메서드 호출
        NSString *copyString = [NSString getString];
        NSLog(@"Accessing Category: %@", copyString); // 출력: Accessing Category: 문자열 카테고리 추가

    }
    return 0;
}


Extension

주로 클래스의 private 인터페이스를 확장하는 데 사용된다. Extension은 클래스의 구현 파일(.m)에서만 사용되며, 다른 파일에서는 접근할 수 없고 해당 클래스 내에서만 접근할 수 있다.

// 선언부
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong, readonly) NSString *name; // readonly 프로퍼티

- (void)publicMethod:(NSString *)name; // public 메서드

@end

// 구현부
#import "Person.h"

// 클래스 확장
@interface Person ()

@property (nonatomic, strong, readwrite) NSString *name; // 확장에서 readwrite로 변경
@property (nonatomic, assign) NSInteger age; // private 속성
- (void)privateMethod; // private 메서드

@end

@implementation Person

- (instancetype)init {
    self = [super init];
    if (self) {
        _name = @"Unknown";
        _age = 0;
    }
    return self;
}

- (void)publicMethod:(NSString *)name {
    self.name = name;
}

- (void)privateMethod {
    NSLog(@"This is a private method.");
}

@end


// 사용부
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        
        // 초기값 확인
        NSLog(@"Initial name: %@", person.name); // 출력: Initial name: Unknown
         
        // 이름 설정을 위한 publicMethod 호출
        [person publicMethod:@"John"];
        
        // 변경된 이름 확인
        NSLog(@"Updated name: %@", person.name); // 출력: pdated name: John 
    }
    return 0;
}

0개의 댓글