DADAHAE's Log
비싼 장난감 가지고 노는 중 (❁´▽`❁)*✲゚*
[TIL] 22-10-11: Objective-C (struct, typedef, class, extension)
22-10-11 화요일
5주차

 

 

 

 

 

 

 

 

WARNING
⚠️ 경고 ⚠️

이곳은 고대 언어 발굴 현장입니다. 
C언어 계열에 알러지가 있으신 분은 특히 주의 바랍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

✔️ Objective-C

1️⃣ Structs

구조체 (소문자)
  • Objc → struct
    • 소문자로 시작
    • C언어 문법
    • 데이터 묶음
  • Swift → Struct
    • 객체 (대문자니까.. 생각하기 쉽죵)
    • 타입 → 인스턴스

 

  • Objc 배열
    • 같은 종류의 여러 데이터 항목을 보유할 수 있는 변수 유형
  • Objc 구조체
    • 다른 종류의 데이터 항목결합할 수 있는 사용자 정의 데이터 유형
    • 레코드를 나타내는데 사용 (record, C언어에서 하나의 묶음을 의미, DB에서도 사용)
      • 다양한 개념이 묶여있는 단위!
      • 음반도 하나의 레코드라고 부르잖아용.
      • Recode = Field + Field + … + Field
    • struct = record + record + … + record

 

  • 도서관에서 책을 추적하고 싶다고 가정하겠다. 각 책에 대한 다음 속성을 추적할 수 있다.
    • 책 = struct
    • 제목, 작가, 주제, 도서ID = Record
      • 각각은 field

 

Defining a Structures

  • struct 문 사용
  • 간단하게 Swift에서 메소드만 빼고 프로퍼티만 남았다고 생각해도 죠아.
struct [structure tag] {
	member definition;
} [one or more structure variables];

 

  • 구조체 태그
    • 선택사항
  • 구조 변수
    • 선택사항
struct Books {
	NSSting *title;
	NSString *author;
	NSString *subject;
	int book_id;
} book;
  • *NSString에는 이 꼭 붙어야 한다. (이전 시간에 이야기 했음 -> 뭐야 나빼고 언제 말했어~)
  • 구조체 이름 ≠ type 이름 (이라고 보긴 어렵다)
    • type은 swift에서나 쓰이는 말이지.. C에서는 no.

 

Accessing Structures Members

  • 구조체 멤버에 접근하기 위해서 멤버 엑세스 연산자(.) 사용
  • 타입이름이라고 보기 힘든 이유
    • Books라고 쓸 수 있으나, 이것들 사용하기 위해서는 struct 키워드를 적어줘야 한다.
    • Books 라는 이름을 가진 struct를 만들겠다는 의미.
    • 이런 경우 struct 키워드 안쓰면 큰일남~
#import <Foundation/Foundation.h>

struct Books {
    NSString *title;
    NSString *author;
    NSString *subject;
    int bookId;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct Books book1;
        struct Books book2;
        
        book1.title = @"C언어 프로그래밍";
        book1.author = @"데니스 리치";
        book1.subject = @"C언어 학습 공식도서";
        book1.bookId = 1234567;
        
//        NSLog(@"%@", book1);   // book1은 인스턴스가 아니라 '구조체' 그 자체여서 안된다.
        NSLog(@"%@", book1.title);
        NSLog(@"%@", book1.author);
        NSLog(@"%@", book1.subject);
        NSLog(@"%d", book1.bookId);
    }
    return 0;
}

 

 

더보기

구조체 이름이 Books인 구조체를 선언할 때, struct 키워드를 안쓰면 어케 되나용?

 

컴파일러가 쓰라고 합니다.

 

 

Structures as Function Arguments

함수의 인수로서의 구조체
  • 다른 변수나 포인터를 전달할 때와 매우 유사한 방식으로 구조체를 함수 인수로 전달할 수 있다.
    • book1을 함수에 전달하려면 어떻게 해야 할까?
  • 앞의 예시처럼 유사하게 접근한다.
#import <Foundation/Foundation.h>

struct Books {
    NSString *title;
    NSString *author;
    NSString *subject;
    int bookId;
};

@interface SampleClass: NSObject
- (void)printBookInfo:(struct Books)book;  // Books는 타입 이름이 아니라 구조체 이름이어서 struct 키워드가 빠질 수 없다.
@end

@implementation SampleClass

- (void)printBookInfo:(struct Books)book {
    NSLog(@"%@", book.title);
    NSLog(@"%@", book.author);
    NSLog(@"%@", book.subject);
    NSLog(@"%d", book.bookId);
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct Books book1;
        struct Books book2;
        
        book1.title = @"C언어 프로그래밍";
        book1.author = @"데니스 리치";
        book1.subject = @"C언어 학습 공식도서";
        book1.bookId = 1234567;
        
        SampleClass *sampleClass = [[SampleClass alloc] init];
        [sampleClass printBookInfo:book1];
    }
    return 0;
}

 

Pointers to Structures

  • 구조체에 대한 포인터를 정의
    • 다른 변수에 대한 포인터를 정의하는 것과 매우 유사한 방식으로 정의 가능
struct Books *struct_pointer;
  • struct_pointer 포인터 변수 안에 구조체 변수의 주소(+크기) 저장 가능

 

  • 구조 변수의 주소를 찾으려면
    • 구조 이름 앞에 & 연산자
struct_pointer = &Book1;

 

  • 해당 구조에 대한 포인터를 사용하여 구조체의 멤버에 접근하려면
    • -> 연산자 사용
struct_pointer->auther;

 

 

더보기

class 내부 함수로 받는 매개변수가 포인터 변수일때, 호출 시 &연산자 안쓰면...

 

더보기

구조체에 대한 포인터 변수로 멤버에 접근하려면...

 

 

#import <Foundation/Foundation.h>

struct Books {
    NSString *title;
    NSString *author;
    NSString *subject;
    int bookId;
};

@interface SampleClass: NSObject
- (void)updateBookInfoTitle:(struct Books)book;
- (void)printBookInfo:(struct Books)book;

- (void)descriptionBookInfo:(struct Books *)book;
- (void)updateBookTitle:(struct Books *)book;
@end

@implementation SampleClass

- (void)updateBookInfoTitle:(struct Books)book {  // 값으로 가져왔을 때 내용을 바꾸면 어떻게 될까
    book.title = @"HelloWorld";
}

- (void)printBookInfo:(struct Books)book {
    NSLog(@"%@", book.title);
    NSLog(@"%@", book.author);
    NSLog(@"%@", book.subject);
    NSLog(@"%d", book.bookId);
}

- (void)updateBookTitle:(struct Books *)book {  // 포인터로 가져왔을 때 내용을 바꾸면 어떻게 될까
    book->title = @"Hello Objective-C";
}

- (void)descriptionBookInfo:(struct Books *)book {
    NSLog(@"%@", book->title);
    NSLog(@"%@", book->author);
    NSLog(@"%@", book->subject);
    NSLog(@"%d", book->bookId);
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct Books book1;
        struct Books book2;
        
        book1.title = @"C언어 프로그래밍";
        book1.author = @"데니스 리치";
        book1.subject = @"C언어 학습 공식도서";
        book1.bookId = 1234567;
        
        SampleClass *sampleClass = [[SampleClass alloc] init];
        
        // 값으로 복제시킬 매개변수 - call by value
        [sampleClass updateBookInfoTitle:book1];
        [sampleClass printBookInfo:book1];
        
        // 포인터 참조값으로 알려주고 직접 건드리게 할 매개변수 - call by reference
        [sampleClass updateBookTitle:&book1];
        [sampleClass descriptionBookInfo:&book1];
    }
    return 0;
}

 

Bit Fields

  • 비트 필드를 사용하면 구조에서 데이터를 패킹할 수 있다.
    • 메모리, 데이터 저장이 중요할 때 유용하다.
  • Low한 영역으로 갈 때 필요하므로 통신 개발을 할 때 사용.. 우리가 거의 건드릴 일이 없을 것.

 

 

2️⃣ Preprocessors

전처리기
  • 컴파일러의 일부가 아니지만, 컴파일 프로세스의 별도 단계이다.
    • C언어 스타일에서 넘어왔기 때문에 남아있는 것.
    • Swift에서는 안쓴다.
  • 간단히 말해서 ‘텍스트 대체 도구’임
  • 실제 컴파일 전에 필요한 전처리를 수행하도록 컴파일러에게 지시함
  • OCPP 라고 불림 (in objc)
  • 파운드 기호(#)로 시작
  • #ifdef, #ifndef 와 같은 애들은 언제 쓸까?
    • iOS 버전이 달라지면 그런 작업을 위해서 작성한다.
  • 이런 애들은 실행하기 전에는 어디가 잘못되었는지 모름.

 

 

Preprocessors Examples

#define MAX_ARRAY_LENGTH 20
  • OCPP에 MAX_ARRAY_LENGTH를 20으로 바꾸라고 지시함

 

#import <Foundation/Foundation.h>
#include "myheader.h"
  • import : 현재 소스 파일에 텍스트를 추가하도록 함
  • include : 현재 소스…

 

  • #undef
  • #ifndef, #endif
#ifdef DEBUG
	/* Your debugging statements here */
#endif
  • 디버그 모드일 때 추가적인 일.. 수행 가능

 

Predefined Macros

  • 날짜 관련
  • 이런거 요즘 잘 안씀, 근데 기계적으로 실행하는 거라 빠름, 그러나 권장되는 사항x

 

Preprocessors Operators

  • 토큰 붙여넣기(##)
  • 정의된 () 연산자
  • 매개변수화된 매크로

 

 

3️⃣ typedef

  • swift의 type alias를 생각하면 좀 쉽지 않을까 ㅎㅎ

typedef

  • typedef 키워드를 제공, 새로운 타입을 지정하는데 사용할 수 있다.
// 1 바이트 숫자에 대해 BYTE 라는 용어를 정의하는 예
typedef unsigned char BYTE;

 

  • 관례에 따라 대문자로 쓰지만, 소문자로도 사용할 수 있다.
typedef unsigned char byte;

 

  • typedef로 구조체 타입을 선언할 수 있다.
/*
 struct Books {
     NSString *title;
     NSString *author;
     NSString *subject;
     int bookId;
 };
 
 typedef struct Books BOOK;

이렇게 나눠서 쓸 걸 한방에 쓰기 위해서..
*/

typedef struct Books {
    NSString *title;
    NSString *author;
    NSString *subject;
    int bookId;
} BOOK;

 

더보기

근데 사실 typedef를 사용하지 않고 struct 구조 정의만 해줘도 구조체를 사용하는데는 지장이 없는 것 같은데, typedef를 사용함으로써 '타입'이라는걸 의미적으로 부여한다 라고 봐도 될까요?

 

네, 순수한 타입인거처럼 쓰는 효과를 내는거죠. 물론 우리가 실제 개발과정에서는 저렇게 타입화 된 구조체를 쓰기보단 직접 클래스로 같은 역할 하는 인스턴스를 만들어 메소드까지 정의해 쓰는 편이 여러가지로 좋습니다.

 

더보기

typedef를 사용해서 BOOKS라고 한 이상 구조체 이름 (Books)을 따로 불러서 사용할 수는 없는건가요?

 

기존처럼 구조체 Books는 유효하고, typedef문이 함께 복합적으로 축약된 것 뿐이지요

 

typedef vs #define

  • #define → 전처리기
  • typedef → 컴파일러
#define TRUE 1
#define FALSE 0

int main() {
	NSLog(@"Value of TRUE : %d\\n", TRUE);
	NSLog(@"Value of FALSE : %d\\n", FALSE);
}

 

typedef and Structure

  • 구조체는 한번 선언해놓고 자주 반복하여 사용하게 되므로, typedef로 구조체 타입을 사용하는 것이 일반적임
  • typedef는 타입 선언에 해당하는 키워드
    • 이것으로 정의해두면 여느 데이터 타입과 다르지 않게 사용할 수 있어 편리함
#import <Foundation/Foundation.h>

// Person 구조체를 정의해서 타입 이름으로 쓰고 싶다면?
typedef struct Person {
    float height;
    int weight;
} Person;

// BMI 계산
float calcBMI(Person person) {
    return person.weight / (person.height * person.height);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // struct Person person;
        Person person;
        person.height = 1.65;
        person.weight = 55;
        
        calcBMI(person);
        NSLog(@"height %f", person.height);
        NSLog(@"weight %d", person.weight);
        NSLog(@"BMI : %f", calcBMI(person));
    }
    return 0;
}

 

 

4️⃣ Type casting

한 데이터 유형 → 다른 데이터 유형 변환
  • 캐스트 연산자를 사용하면 명시적으로 캐스팅 가능
(type_name) expression
// 괄호 필수!
  • Objc : 일반적으로 32비트의 경우 float의 기본 유형에서 파생된 부동 소수점 연산을 수행하기 위해 CGFloat를 사용하고 64비트의 경우 double을 사용함

 

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int sum = 17;
        int count = 5;
        
        // int와 int의 나눗셈이라 int라고 결과를 생각할 수 있음
        NSLog(@"Value of mean : %d", (sum / count));
        
        // 강제로 위 계산 결과를 float값으로 각각 타입캐스팅 변환처리해준다면? 결과는 float 타입이 된다.
        NSLog(@"Value of mean : %f", ((float)sum / (float)count));
    }
    return 0;
}
  • 컴파일러에게 자동으로 수행되는 암시적 type casting
  • 캐스트 연산자를 사용하는 명시적 type cating

 

Interger Promotion

정수 승격

 

 

Usual Arithmetic Conversion

  • 일반적인 산술 변환은 해당 값을 공통 유형으로 캐스팅하기 위해 암시적으로 수행된다.
  • 올라가면 더 큰 수.
  • 피연산자의 유형이 여전히 다른 경우 다음 계층에서 가장 높은 유형으로 반환된다.
  • 이 순서로 알아서 계산되는 C언어의 약속

 

 

 

5️⃣ Log

NSLog method

출력과 로그는 다르다.
  • 로그를 출력하기 위해 NSLog 메서드 사용
  • Swift에서는 dump가 비슷한 역할

Disabling logs in Live apps

  • NSLogs 때문에 디바이스 로그에 출력되고 라이브 빌드에서 로그를 인쇄하는 것은 좋지 않다.
  • 개발단계에서 이렇게 하고, 요즘엔 이렇게 안함

 

 

6️⃣ Error Handling

Error Handling

  • Foundation 프레임워크에서 사용할 수 있는 NSError 클래스와 함께 제공된다.
  • NSError 객체는 오류 코드 또는 오류 문자열만 사용하여 가능한 것보다 더 풍부하고 확장가능한 오류 정보를 캡슐화한다.
  • NSError 객체의 핵심 속성은 오류 도메인, 도메인 특정 오류 코드 및 응용 프로그램 특정 정보를 포함하는 사용자 딕셔너리다.

NSError

  • NSError 사용하여 런타임 오류 정보 전달
  • NSError
    •  도메인 : 미리 정의된 NSError 도메인 중 하나이거나 사용자 정의 도메인을 설명하는 임의의 문자열일 수 있다.
      • 도메인은 nil이 아니어야 함
    • 코드 : 오류에 대한 오류 코드
    • 사용자 정보 : 오류 및 userInfo에 대한 userInfo 사전은 nil일 수 있다.
  • 사용자 지정 오류 생성 방법
NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
NSString *desc = NSLocalizedString(@"Unable to complete the process", @"");
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];

 

 

 

7️⃣ Classes & Objects

  • Objc 프로그래밍 언어의 주요 목적
    • C 프로그래밍 + 객체지향(Small Talk)
  • 클래스는 @interface@implementation이라는 2개의 다른 섹션에 정의된다.
  • 거의 모든 것이 객체 형태
    • 객체는 메시지 수신(method call), 종종 수신자라고 함
    • 객체는 인스턴스 변수를 포함함
    • 객체 및 인스턴스 변수에는 범위가 있음
  • 클래스는 객체의 구현 숨김
  • Property는 다른 클래스의 클래스 인스턴스 변수에 대한 접근을 제공하는데 사용된다

 

Class Definitions

  • 클래스를 정의할 때 데이터 유형에 대한 청사진
  • @interface 키워드 + 인터페이스(클래스)이름
    • 한쌍의 중괄호로 묶인 클래스는 본문
    • Objective-C 에서 모든 클래스는 NSObject 라는 기본 클래스에서 파생된다.
  • 모든 Objc 클래스의 상위 클래스
@interface Box: NSObject {
	// Instance variables(비공개)
	double length;   // Length of a box
	double breadth;  // Breadth of a box
}
@property(nonatomic, readwrite)double height; // Property

@end

 

Allocating and Initializing Objects

  • 클래스는 객체에 대한 청사진을 제공하므로 기본적으로 객체는 클래스에서 생성된다.
  • 기본 유형의 변수를 선언하는 것과 정확히 같은 종류의 선언으로 클래스의 객체 선언함.
  • box1, box2 객체 모두 데이터 멤버의 고유한 복사본을 가진다.
#import <Foundation/Foundation.h>

@interface Box : NSObject {
    double length;  // 길이
    double breadth; // 폭
    double height;  // 높이
}
//@property(nonatomic, readwrite) double length;
//@property(nonatomic, readwrite) double breadth;
@property(nonatomic, readwrite) double height;

- (double)volume;

@end

@implementation Box

// 선언된 프로퍼티를 가장 쉽게 구현부에서 만들어주는 방법
//@synthesize length;
//@synthesize breadth;
@synthesize height;

// 초기화 처리후 반환될 인스턴스는 id라고 통칭된다.
- (id)init {
    self = [super init];
    self->length = 1.0;
    self->breadth = 1.0;
    return self;
}

- (double)volume {
    return length * breadth * height;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Box *box1 = [[Box alloc] init];
        Box *box2 = [[Box alloc] init];
        
        box1.height = 5.0;
        box2.height = 10.0;
        
        NSLog(@"Volume of Box1 : %f", [box1 volume]);
        NSLog(@"Volume of Box2 : %f", [box2 volume]);
    }
    return 0;
}

 

Properties

  • 클래스 외부에서 클래스의 인스턴스 변수에 접근할 수 있도록 objc에 도입
  • @property 키워드로 속성을 표기한다.
    • 그 뒤에는 nonatomic, atomic, readwrite, readonly, strong, unsafe_unretained, weak 등이 온다.
    • 그 다음 변수의 데이터 유형
    • 세미콜론으로 끝나는 속성 이름
  • 구현 클래스에 synthesize 문을 추가할 수 있다.
    • 최근 Xcode에서는 합성 부분을 Xcode로 처리하므로 추가할 필요가 없다.
  • 클래스의 인스턴스 변수에 접근할 수 있는 속성에서만 가능
    • 실제 속성에 대해 내부적으로 getter, setter 메소드가 생성된다.

예) @property(nonatomic, readonly) BOOL is Done 속성이 있다고 가정

- (void)setIsDone(BOOL)isDone;
- (BOOL)isDone;

 

*Inheritance

  • OOP에서 가장 중요한 개념 중 하나 : 상속
  • 상속을 통해 다른 클래스로 클래스 정의 가능
    • 애플리케이션을 더 쉽게 만들고 유지 가능
    • 코드의 빠른 개발과 시간 단축
  • 클래스를 생성할 때 새로운 데이터 멤버와 멤버 함수(method)를 작성하는 대신, 새 클래스가 기존 클래스의 멤버를 상속하도록 지정할 수 있다.
    • super class - sub class
  • 상속을 구현한다는 아이디어 : 관계
    • 포유류 IS-A 동물
      • 동물 HAS-A 포유류
    • IS-A 포유류
      • 포유류 HAS-A
    • IS-A 동물
      • 동물 HAS-A

 

Base & Derived Classes

  • objc 는 다단계 상속만 허용함
    • 기본 클래스는 하나만 가질 수 있지만, 다단계 상속은 허용
#import <Foundation/Foundation.h>

// Person 클래스 정의
@interface Person : NSObject {
    NSString *personName;
    NSInteger personAge;
}
// (id)는 instance를 의미하는 것으로 약속함. init 전용 return값이라고 생각하면 될 듯
- (id)initWithName:(NSString *)name andAge:(NSInteger)age;
- (void)print;
@end

@implementation Person

- (id)initWithName:(NSString *)name andAge:(NSInteger)age {
    self = [super init];
    
    personName = name;
    personAge = age;
    
    return self;
}

- (void)print {
    NSLog(@"Name: %@", personName);
    NSLog(@"Age: %ld", personAge);
}

@end

// Person을 상속받은 Employee 클래스 정의
@interface Employee : Person {
    NSString *employeeEducation;
}
- (id)initWithName:(NSString *)name andAge:(NSInteger)age andEducation:(NSString *)education;
- (void)print;
@end

@implementation Employee: Person

- (id)initWithName:(NSString *)name andAge:(NSInteger)age andEducation:(NSString *)education {
    self = [super initWithName:name andAge:age];
    employeeEducation = education;
    return self;
}
- (void)print {
    [super print];
    NSLog(@"Education: %@", employeeEducation);
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *person = [[Person alloc] initWithName:@"Ned" andAge:13];
        [person print];
        
        Employee *employee = [[Employee alloc] initWithName:@"Dadahae" andAge:24 andEducation:@"대학원"];
        [employee print];
    }
    return 0;
}

 

더보기

Swift로 바꿔보자.

 

// 클래스 정의 Person
class Person {
    // Property
    var personName: String = ""
    var personAge: Int = 0
    
    // initializer
    init(name: String, age: Int) {
        personName = name
        personAge = age
    }
    
    // Method
    func description() {
        print("Name: \(personName)")
        print("Age: \(personAge)")
    }
}

// 클래스 정의 Employee
class Employee: Person {
    var employeeEducation: String = ""
    
    init(name: String, age: Int, education: String) {
        super.init(name: name, age: age)
        employeeEducation = education
    }
    
    override func description() {
        super.description()
        print("Education: \(employeeEducation)")
    }
}

let person: Person = Person(name: "dadahae", age: 24)
person.description()

let employee: Employee = Employee(name: "kangvic", age: 24, education: "대학원")
employee.description()

 

 

Access Control and Inheritance

  • 서브 클래스는 인터페이스 클래스스에 정의된 경우,
    • 기본 클래스의 모든 private 멤버에 접근할 수 있지만
    • 구현 파일에 정의된 private 멤버에는 접근 불가

 

🦁 2022년에 Objective-C 와 Swift?
https://stevenpcurtis.medium.com/objective-c-or-swift-in-2022-4416ee0cc7b5 
objc에서는 암호화된 API를 만들 수 있어서 아직 사용하는 보안업체들이 있다. 

 

 

 

 

*Polymorphism

다형성
  • 다형성이라는 단어는 많은 형태를 갖는다는 의미
  • 일반적으로 다형성은 클래스의 계층 구조가 있고 상속으로 관련되어 있을 때 발생
  • objc 다형성은 멤버 함수에 대한 호출이 함수를 호출하는 객체의 유형에 따라 다른 함수가 실행되도록 한다는 것을 의미함

 

예) 모든 모양에 대한 기본 인터페이스를 제공하는 Shape 클래스가 있다. Square 및 Rectangle은 기본 클래스인 Shape에서 파생된다.

  • Shape (super)
    • Square (sub)
    • Rectangle (sub)

 

  • 다형성은 두 클래스의 메서드 구현을 기반으로 기본 클래스(super)와 파생 클래스(sub) 간의 메서드 전환을 처리한다.
#import <Foundation/Foundation.h>

// 윤곽 클래스 정의
@interface Shape: NSObject {
    CGFloat area;
}
- (void)printArea;
- (void)calculateArea;
@end

@implementation Shape
- (void)printArea {
    NSLog(@"The area is %f", area);
}
- (void)calculateArea {
    // 아직 모르겠음
}
@end

// 윤곽을 상속받은 직사각형 클래스 정의
@interface Square : Shape {
    CGFloat length;
}
- (id)initWithSide:(CGFloat)side;
- (void)calculateArea;
@end

@implementation Square
    - (id)initWithSide:(CGFloat)side {
        self = [super init];
        length = side;
        return self;
    }
    - (void)calculateArea {
        area = length * length;
    }
@end

// 윤곽을 상속받은 직사각형 클래스
@interface Rectangle : Shape {
    CGFloat length;
    CGFloat breadth;
}
- (id)initWithLength:(CGFloat)length andBreadth:(CGFloat)breadth;
@end

@implementation Rectangle

- (id)initWithLength:(CGFloat)length andBreadth:(CGFloat)breadth {
    self = [super init];
    length = length;
    breadth = breadth;
    return self;
}

- (void)calculateArea {
    area = length * breadth;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Shape *shape = [[Shape alloc] init];
        [shape printArea];
        
        Square *square = [[Square alloc] initWithSide:10.0];
				[square calculateArea];
        [square printArea];
        
        Rectangle *rectangle = [[Rectangle alloc] initWithLength:10.0 andBreadth:5.0];
        [rectangle calculateArea];
        [rectangle printArea];
    }
    return 0;
}

 

더보기

Swift로 바꿔보기

 

class Shape {
    var area: CGFloat = 0.0
    
    func printArea() {
        print("The area is \(area)")
    }
    func calculateArea() {
        
    }
}

class Square: Shape {
    var length: CGFloat = 0.0
    
    init(side: CGFloat) {
        length = side
        super.init()
    }
    override func calculateArea() {
        area = length * length
    }
}

class Rectangle: Shape {
    var length: CGFloat = 0.0
    var breadth: CGFloat = 0.0
    
    init(length:CGFloat, breadth: CGFloat) {
        self.length = length
        self.breadth = breadth
        super.init()
    }
    
    override func calculateArea() {
        area = length * breadth
    }
}

let shape: Shape = Shape()
shape.printArea()

let square: Square = Square(side: 10.0)
square.calculateArea()
square.printArea()

let rectangle: Rectangle = Rectangle(length: 10.0, breadth: 5.0)
rectangle.calculateArea()
rectangle.printArea()

 

 

더보기

Q. Shape class에서 init을 만들어주지 않았는데도 상속클래스에서 init 작성할때 super를 해주는 이유는 혹시나 나중에라도 Shape에서 init이 작성될때를 위함인가요?

A. 네. 습관적으로 하시는게 좋습니다. 안전한 코드를 위하여!

 

Q. func calculateArea의 경우에도 같은 이유겠군요?

A. 네 맞습니다.

 

 

*Data Encapsulation

  • Objc-C 프로그램의 2가지 기본 요소
    • 프로그램 구문(코드)
    • 프로그램 데이터
  • 캡슐화
    • 데이터와 데이터를 조작하는 기능을 함께 묶고 외부 간섭과 오용으로부터 안전하게 유지하는 OOP 개념
    • 데이터 캡슐화는 데이터 은닉이라는 중요한 OOP 개념으로 이어짐
  • 데이터 캡슐화
    • 데이터와 이를 사용하는 기능을 묶는 메커니즘
  • 데이터 추상화
    • 인터페이스만 노출하고 구현 세부 사항을 사용자에게 숨기는 메커니즘

 

  • Objc는 클래스‘캡슐화 및 데이터 은닉’ 속성 지원
@interface Adder: NSObjcet {
	NSInteger total;    // private
}
- (id) ~           // public
- (void) ~ 
- (NSInteger) ~
@end
  • total 변수
    • 비공개
    • 클래스 외부에서 접근x
    • Adder 클래스의 다른 멤버만 접근o, 프로그램의 다른 부분에서 접근x
    • 캡슐화를 위한 방법 중 하나
    • 가끔 비공개를 위해서 아예 interface에 안적고 implementation에만 적기도 한다. interface는 외부에 공개하려고…
  • 인터페이스 파일 내의 메서드
    • 접근o
    • 범위 내에서 공개된다

 

Data Encapsulation Example

#import <Foundation/Foundation.h>

@interface Adder : NSObject
//@property(nonatomic, readonly) NSInteger total;

- (id)initWithInitialNumber:(NSInteger)initialNumber;
- (void)addNumber:(NSInteger)newNumber;
- (NSInteger)getTotal;

@end

@implementation Adder {
    NSInteger total;
}

//@synthesize total;

- (id)initWithInitialNumber:(NSInteger)initialNumber {
    self = [super init];
    total = initialNumber;
    return self;
}

- (void)addNumber:(NSInteger)newNumber {
    total = total + newNumber;
}

- (NSInteger)getTotal {
    return total;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Adder *adder = [[Adder alloc] initWithInitialNumber:10];
        [adder addNumber:5];
        [adder addNumber:4];
        
//        NSLog(@"The total is %ld", adder.total);
        NSLog(@"The total is %ld", [adder getTotal]);
    }
    return 0;
}
  • private 멤버 total은 숨겨져 있지만 내부 계산에서는 꼭 필요하다!

 

Designing Strategy

  • 좋은 캡슐화
    • 실제로 노출해야 하는 경우가 아니면 클래스 멤버를 비공개로 설정한다.

 

 

 

8️⃣ Extension (확장)

8-1. Category

extension과 비슷하면서도 다르다.

  • 특정 상황에서만 동작을 추가하여 기존 클래스를 확장하려는 경우가 있다!
    • 카테고리
    • 익스텐션
    을 제공한다.
  • 기존 클래스에 메서드를 추가하는 경우, 가장 쉬운 방법은 ‘카테고리’를 사용하는 것
@interface ClassName (CategoryName)

@end

 

Characteristics of Category

    • 메소드 보다는 + 메소드를 사용하는 경우가 많다.
  • ‘원래 구현 소스 코드가 없더라도’ 모든 클래스에 대해 카테고리 선언 가능
    • 확장하려는 클래스의 구현부가 없어도 카테고리 자체의 구현부를 만들 수 있다는 의미이다.
  • 카테고리에서 선언하는 모든 메서드
    • 원래 클래스의 모든 하위 클래스 + 원래 클래스의 모든 인스턴스에서 사용 가능
  • 런타임에는 카테고리에 의해 추가된 메서드와 원래 클래스에 의해 구현되는 메서드 사이에 차이가 없다
#import <Foundation/Foundation.h>

// 카테고리 만들기 예제
@interface NSString(MyAddtions)
+(NSString *)getCopyRightString;    // 저작권자 이름 알려주는 클래스 메서드
-(NSString *)getName;
@end

@implementation NSString(MyAddtions)

+(NSString *)getCopyRightString {
    return @"저작권자 멋쟁이사자처럼 2022";
}

-(NSString *)getName {
    return @"ned";
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"%@", [NSString getCopyRightString]);
        
        NSString *temp = [[NSString alloc] init];
        NSLog(@"%@", [temp getName]);
    }
    return 0;
}
  • 카테고리에 의해 추가된 메서드는 클래스 및 해당 하위 클래스의 모든 인스턴스에서 사용할 수 있다.
  • 그러나 추가 메서드를 사용하려는 소스 코드 파일에서 카테고리 헤더 파일을 가져와야 한다.
    • 그렇지 않으면 컴파일러 경고 및 오류.

이 예에서는 클래스가 하나뿐이므로 헤더 파일을 포함하지 않았으므로 위와 같은 경우 헤더 파일을 포함해야 합니다.→ ???

 

 

8-2. Extension

  • 클래스 extension은 카테고리와 어느 정도 유사하지만 컴파일 시간에 소스 코드가 있는 클래스에만 추가할 수 있다.
    • 클래스는 클래스 extension과 동시에 컴파일됨
  • 클래스 extension으로 선언된 메서드는…
    • 원래 클래스의 구현 블록에서 구현되므로 예를 들어 NSString과 같은 Cocoa 또는 Cocoa Touch 클래스와 같은 프레임워크 클래스에서 클래스 확장을 선언할 수 없습니다.
  • extension은 실제로 category 이름이 없는 category입니다. 종종 익명 category 라고 합니다.
  • extension을 선언하는 구문은 표준 Objective-C 클래스 설명과 마찬가지로 @interface 키워드를 사용하지만 하위 클래스로부터의 상속을 나타내지는 않는다.
    • 괄호만 추가함
@interface ClassName() 

@end

 

Characteristics of Extensions

  • extension은 모든 클래스에 대해 선언할 수 없으며 소스 코드의 원래 구현이 있는 클래스에만 적용됩니다.
    • extension은 category 처럼 자체 구현부를 가질 수 없다. 확장하려는 클래스의 구현부에다 코드를 작성해야 한다는 의미.
  • extension은 해당 클래스에만 해당하는 개인 메서드와 개인 변수를 추가하는 것입니다.
  • extension 내부에 선언된 메서드나 변수는 상속된 클래스에서도 액세스할 수 없습니다.

 

 

 

 

 

 

 

  Comments,     Trackbacks