Objective-C 둘러보기(3)

jonghwan·2022년 10월 11일
0

멋쟁이사자처럼

목록 보기
25/28
post-thumbnail

구조체(Structures)

Objective-C 배열을 사용하면 같은 종류의 여러 데이터 항목을 보유할 수 있는 변수 유형을 정의할 수 있지만 구조체는 다른 종류의 데이터 항목을 결합할 수 있는 Objective-C 프로그래밍에서 사용할 수 있는 또 다른 사용자 정의 데이터 유형입니다.

구조체는 레코드를 나타내는 데 사용됩니다. 도서관에서 책을 추적하고 싶다고 가정해 보겠습니다. 각 책에 대한 다음 속성을 추적할 수 있습니다.

  • 제목

  • 작가

  • 주제

  • 도서 ID

구조체 정의(Defining a Structure)

구조체를 정의하려면 struct 문을 사용해야 합니다.

struct 문은 프로그램에 대해 둘 이상의 멤버가 있는 새 데이터 유형을 정의합니다.

struct 문의 형식은 다음과 같습니다.

struct [structure tag] {
 member definition;
 member definition;
 …
 member definition;
} [one or more structure variables];

구조체 태그 는 선택 사항이며 각 멤버 정의는 int i와 같은 일반 변수 정의입니다.

또는 float f; 또는 다른 유효한 변수 정의. 구조 정의 끝에서 마지막 세미콜론 앞에 하나 이상의 구조 변수를 지정할 수 있지만 선택 사항입니다.

Book 구조를 선언하는 방법은 다음과 같습니다.

struct Books {
 NSString *title;
 NSString *author;
 NSString *subject;
 int book_id;
} book;

구조체 멤버 접근(Accessing Structure Members)

구조체의 멤버에 액세스하려면 멤버 액세스 연산자(.) 를 사용 합니다.

멤버 액세스 연산자는 구조 변수 이름과 액세스하려는 구조 멤버 사이의 마침표로 코딩됩니다.

구조체 유형의 변수를 정의하려면 struct 키워드를 사용 합니다.

#import <Foundation/Foundation.h>

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

int main() {
 struct Books Book1; /* Declare Book1 of type Book */
 struct Books Book2; /* Declare Book2 of type Book */

 /* book 1 specification */
 Book1.title = @"Objective-C Programming";
 Book1.author = @"Nuha Ali";
 Book1.subject = @"Objective-C Programming Tutorial";
 Book1.book_id = 6495407;
 
 /* print Book1 info */
 NSLog(@"Book 1 title : %@\n", Book1.title);
 NSLog(@"Book 1 author : %@\n", Book1.author);
 NSLog(@"Book 1 subject : %@\n", Book1.subject);
 NSLog(@"Book 1 book_id : %d\n", Book1.book_id);
 
 /* print Book2 info */
 NSLog(@"Book 2 title : %@\n", Book2.title);
 NSLog(@"Book 2 author : %@\n", Book2.author);
 NSLog(@"Book 2 subject : %@\n", Book2.subject);
 NSLog(@"Book 2 book_id : %d\n", Book2.book_id);
 
 return 0;
}

// Book 1 title : Objective-C Programming
// Book 1 author : Nuha Ali
// Book1subject:Objective-CProgrammingTutorial

함수의 인수로서의 구조체(Structures as Function Arguments)

다른 변수나 포인터를 전달할 때와 매우 유사한 방식으로 구조체를 함수 인수로 전달할 수 있습니다.

앞의 예에서 액세스한 것과 유사한 방식으로 구조 변수에 액세스합니다.

#import <Foundation/Foundation.h>

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

@interface SampleClass:NSObject
/* function declaration */
- (void) printBook:( struct Books) book ;
@end

@implementation SampleClass

- (void) printBook:( struct Books) book {
 NSLog(@"Book title : %@\n", book.title);
 NSLog(@"Book author : %@\n", book.author);
 NSLog(@"Book subject : %@\n", book.subject);
 NSLog(@"Book book_id : %d\n", book.book_id);
}

@end

int main() {
 struct Books Book1; /* Declare Book1 of type Book */
 struct Books Book2; /* Declare Book2 of type Book */

 /* book 1 specification */
 Book1.title = @"Objective-C Programming";
 Book1.author = @"Nuha Ali";
 Book1.subject = @"Objective-C Programming Tutorial";
 Book1.book_id = 6495407;
 
 /* book 2 specification */
 Book2.title = @"Telecom Billing";
 Book2.author = @"Zara Ali";
 Book2.subject = @"Telecom Billing Tutorial";
 Book2.book_id = 6495700;

 SampleClass *sampleClass = [[SampleClass alloc]init];
 /* print Book1 info */
 [sampleClass printBook: Book1];
 
 /* Print Book2 info */
 [sampleClass printBook: Book2];
 
 return 0;
}

// Book title : Objective-C Programming
// Book author : Nuha Ali
// Book subject : Objective-C Programming Tutorial
// Book book_id : 6495407
// Book title : Telecom Billing
// Book author : Zara Ali
// Book subject : Telecom Billing Tutorial
// Book book_id : 6495700

구조체에 대한 포인터(Pointers to Structures)

다음과 같이 다른 변수에 대한 포인터를 정의하는 것과 매우 유사한 방식으로 구조에 대한 포인터를 정의할 수 있습니다.

struct Books *struct_pointer;

이제 위에서 정의한 포인터 변수에 구조체 변수의 주소를 저장할 수 있습니다.

구조 변수의 주소를 찾으려면 다음과 같이 구조 이름 앞에 & 연산자를 배치합니다.

struct_pointer = &Book1;

해당 구조에 대한 포인터를 사용하여 구조의 멤버에 액세스하려면 다음과 같이 -> 연산자를 사용해야 합니다.

struct_pointer->title;
#import <Foundation/Foundation.h>

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

@interface SampleClass:NSObject
/* function declaration */
- (void) printBook:( struct Books *) book ;
@end

@implementation SampleClass

- (void) printBook:( struct Books *) book {
 NSLog(@"Book title : %@\n", book->title);
 NSLog(@"Book author : %@\n", book->author);
 NSLog(@"Book subject : %@\n", book->subject);
 NSLog(@"Book book_id : %d\n", book->book_id);
}

@end

int main() {
 struct Books Book1; /* Declare Book1 of type Book */
 struct Books Book2; /* Declare Book2 of type Book */

 /* book 1 specification */
 Book1.title = @"Objective-C Programming";
 Book1.author = @"Nuha Ali";
 Book1.subject = @"Objective-C Programming Tutorial";
 Book1.book_id = 6495407;
 
 /* book 2 specification */
 Book2.title = @"Telecom Billing";
 Book2.author = @"Zara Ali";
 Book2.subject = @"Telecom Billing Tutorial";
 Book2.book_id = 6495700;

 SampleClass *sampleClass = [[SampleClass alloc]init];
 /* print Book1 info by passing address of Book1 */
 [sampleClass printBook:&Book1];
 
 /* print Book2 info by passing address of Book2 */
 [sampleClass printBook:&Book2];
 
 return 0;
}

// Book title : Objective-C Programming
// Book author : Nuha Ali
// Book subject : Objective-C Programming Tutorial
// Book book_id : 6495407
// Book title : Telecom Billing
// Book author : Zara Ali
// Book subject : Telecom Billing Tutorial
// Book book_id : 6495700

비트 필드(Bit Fields)

비트 필드를 사용하면 구조에서 데이터를 패킹할 수 있습니다. 이것은 메모리나 데이터 저장이 중요할 때 특히 유용합니다. 대표적인 예 -

  • 여러 개체를 기계어로 묶는 것. 예를 들어 1비트 플래그를 압축할 수 있습니다.

  • 외부 파일 형식 읽기 -- 비표준 파일 형식을 읽을 수 있습니다. 예: 9비트 정수

Objective-C를 사용하면 변수 뒤에 :bit length를 넣어 구조 정의에서 이를 수행할 수 있습니다.

struct packed_struct {
 unsigned int f1:1;
 unsigned int f2:1;
 unsigned int f3:1;
 unsigned int f4:1;
 unsigned int type:4;
 unsigned int my_int:9;
} pack

여기에서 Packed_struct는 6개의 멤버를 포함합니다.

4개의 1비트 플래그 f1..f3, 4비트 유형 및 9비트 my_int.

Objective-C는 필드의 최대 길이가 컴퓨터의 정수 단어 길이보다 작거나 같은 경우 위의 비트 필드를 가능한 한 압축하여 자동으로 압축합니다.

그렇지 않은 경우 일부 컴파일러는 필드에 대한 메모리 겹침을 허용하는 반면 다른 컴파일러는 다음 단어에 다음 필드를 저장할 수 있습니다.

전처리기(Preprocessors)

Objective-C 전처리기는 컴파일러 의 일부가 아니지만 컴파일 프로세스의 별도 단계입니다.

간단히 말해서 Objective-C 전처리기는 텍스트 대체 도구이며 실제 컴파일 전에 필요한 전처리를 수행하도록 컴파일러에 지시합니다.

Objective-C 전처리기를 OCPP라고 부를 것입니다.

모든 전처리기 명령은 파운드 기호(#)로 시작합니다. 공백이 아닌 첫 번째 문자여야 하며 가독성을 위해 전처리기 지시문은 첫 번째 열에서 시작해야 합니다.

전처리기 예제(Preprocessors Examples)

다음 예를 통해 다양한 전처리기를 알아봅시다.

#define MAX_ARRAY_LENGTH 20

위 지시문은 OCPP에 MAX_ARRAY_LENGTH의 인스턴스를 20으로 바꾸도록 지시합니다.

가독성을 높이려면 상수에 #define 을 사용하세오.

#import <Foundation/Foundation.h>
#include "myheader.h"

이 지시문은 OCPP에게 Foundation Framework 에서 Foundation.h를 가져와 현재 소스 파일에 텍스트를 추가하도록 지시합니다.

다음 줄은 OCPP에게 로컬 디렉토리에서 myheader.h 를 가져와서 현재 소스 파일에 내용을 추가하도록 지시합니다.

#undef FILE_SIZE
#define FILE_SIZE 42

이것은 OCPP에 기존 FILE_SIZE를 정의 해제하고 42로 정의하도록 지시합니다.

#ifndef MESSAGE
 #define MESSAGE "You wish!”
#endif

이것은 MESSAGE가 아직 정의되지 않은 경우에만 MESSAGE를 정의하도록 OCPP에 지시합니다.

#ifdef DEBUG
 /* Your debugging statements here */
#endif

이것은 DEBUG가 정의된 경우 동봉된 명령문을 처리하도록 OCPP에 지시합니다. 이것은 컴파일 시 -DDEBUG 플래그를 gcc 컴파일러에 전달하는 경우에 유용합니다. 이것은 DEBUG를 정의하므로 컴파일하는 동안 즉시 디버깅을 켜고 끌 수 있습니다.

미리 정의된 매크로(Predefined Macros)

전처리기 예제(Preprocessors Examples)

#import <Foundation/Foundation.h>

int main() {
 NSLog(@"File :%s\n", __FILE__ );
 NSLog(@"Date :%s\n", __DATE__ );
 NSLog(@"Time :%s\n", __TIME__ );
 NSLog(@"Line :%d\n", __LINE__ );
 NSLog(@"ANSI :%d\n", __STDC__ );
 
 return 0;
}

// File :main.m
// Date :Oct 7 2022
// Time :04:46:14
// Line :8
// ANSI :1

전처리기 연산자(Preprocessors Operators)

Objective-C 전처리기는 매크로 생성에 도움이 되는 다음 연산자를 제공합니다.

매크로 연속 ( \ )

매크로는 일반적으로 한 줄에 포함되어야 합니다.

매크로 연속 연산자는 한 줄에 너무 긴 매크로를 계속하는 데 사용됩니다.

#define message_for(a, b) \
 NSLog(@#a " and " #b ": We love you!\n")

문자열화(#)

문자열화 또는 숫자 기호 연산자('#')는 매크로 정의 내에서 사용될 때 매크로 매개변수를 문자열 상수로 변환합니다.

이 연산자는 지정된 인수 또는 매개변수 목록이 있는 매크로에서만 사용할 수 있습니다.

#import <Foundation/Foundation.h>

#define message_for(a, b) \
 NSLog(@#a " and " #b ": We love you!\n”)
 
int main(void) {
 message_for(Carole, Debra);
 
 return 0;
}

// Carole and Debra: We love you!

토큰 붙여넣기(##)

매크로 정의 내의 토큰 붙여넣기 연산자(##)는 두 인수를 결합합니다.

매크로 정의에 있는 두 개의 개별 토큰을 단일 토큰으로 결합할 수 있습니다.

#import <Foundation/Foundation.h>

#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)

int main(void) {
 int token34 = 40;

 tokenpaster(34); // NSLog (@"token34 = %d", token34);
 
 return 0;
}

// token34 = 40

정의된 ( ) 연산자

전처리기 정의 연산자는 식별자가 #define을 사용하여 정의되었는지 확인하기 위해 상수 표현식에서 사용됩니다.

지정된 식별자가 정의된 경우 값은 true(0이 아님)입니다.

기호가 정의되지 않은 경우 값은 거짓(영)입니다. 정의된 연산자는 다음과 같이 지정됩니다.

#import <Foundation/Foundation.h>

#if !defined (MESSAGE)
 #define MESSAGE "You wish!"
#endif

int main(void) {
 NSLog(@"Here is the message: %s\n", MESSAGE);
 
 return 0;
}

// Here is the message: You wish!

매개변수화된 매크로

OCPP의 강력한 기능 중 하나는 매개변수화된 매크로를 사용하여 기능을 시뮬레이션하는 기능입니다.

예를 들어 다음과 같이 숫자를 제곱하는 코드가 있을 수 있습니다.

int square(int x) {
 return x * x;
}

다음과 같이 매크로를 사용하여 위의 코드를 다시 작성할 수 있습니다.

#define square(x) ((x) * (x))

인수가 있는 매크로는 사용 하기 전에 #define 지시문을 사용하여 정의해야 합니다.

인수 목록은 괄호로 묶여 있으며 매크로 이름 바로 뒤에 와야 합니다.

매크로 이름과 여는 괄호 사이에는 공백이 허용되지 않습니다.

#import <Foundation/Foundation.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void) {
 NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));
 
 return 0;
}

// Max between 20 and 10 is 20

typedef

Objective-C 프로그래밍 언어는 typedef 라는 키워드 를 제공하여 유형에 새 이름을 지정하는 데 사용할 수 있습니다.

다음은 1바이트 숫자에 대해 BYTE 라는 용어를 정의하는 예입니다.

typedef unsigned char BYTE;

이 유형 정의 후에 식별자 BYTE를 unsigned char 유형의 약어로 사용할 수 있습니다.

BYTE b1, b2;

관례에 따라 대문자는 이러한 정의에 사용되어 사용자에게 유형 이름이 실제로는 기호 약어임을 상기시키지만 다음과 같이 소문자를 사용할 수 있습니다.

typedef unsigned char byte;

typedef 를 사용하여 사용자 정의 데이터 유형에도 이름을 지정할 수 있습니다 .

예를 들어, 구조체와 함께 typedef를 사용하여 새 데이터 형식을 정의한 다음 해당 데이터 형식을 사용하여 다음과 같이 직접 구조체 변수를 정의할 수 있습니다.

#import <Foundation/Foundation.h>

typedef struct Books {
 NSString *title;
 NSString *author;
 NSString *subject;
 int book_id;
} Book;

int main() {
 Book book;
 book.title = @"Objective-C Programming";
 book.author = @"TutorialsPoint";
 book.subject = @"Programming tutorial";
 book.book_id = 100;

 NSLog( @"Book title : %@\n", book.title);
 NSLog( @"Book author : %@\n", book.author);
 NSLog( @"Book subject : %@\n", book.subject);
 NSLog( @"Book Id : %d\n", book.book_id);
 
 return 0;
}

// Book title : Objective-C Programming
// Book author : TutorialsPoint
// Book subject : Programming tutorial
// Book Id : 100

typedef와 #define 비교

#define 은 Objective-C 지시문으로 typedef 와 유사 하지만 다음과 같은 차이점이 있는 다양한 데이터 유형에 대한 별칭을 정의하는 데 사용됩니다.

typedef 는 유형에만 기호 이름을 부여하는 것으로 제한되는 반면 #define 은 1을 ONE으로 정의할 수 있는 것처럼 값의 별칭을 정의하는 데에도 사용할 수 있습니다.

typedef 해석은 #define 문이 전처리기에 의해 처리되는 컴파일러에 의해 수행됩니다.

#import <Foundation/Foundation.h>

#define TURE 1
#define FALSE 0

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

// Value of TRUE : 1
// Value of FALSE : 0

구조체는 한 번 선언해놓고 자주 반복하여 사용하게 되므로, 구조체 타입을 typedef로 만들어 사용하는 것이 일반적이다.

typedef는 타입 선언에 해당하는 키워드로, typedef로 정의해 두면 여느 데이터 타입과 다르지 않게 사용할 수 있어 편리하다.

이렇게 해야 구조체를 다른 함수에 파라미터 넘겨주고 활용하기 편하다.

타입 캐스팅(Type Casting)

타입 캐스팅은 한 데이터 유형에서 다른 데이터 유형으로 변수를 변환하는 방법입니다.

예를 들어, 긴 값을 간단한 정수에 저장하려면 long to int를 입력할 수 있습니다.

다음과 같이 캐스트 연산자 를 사용하여 명시적으로 값을 한 유형에서 다른 유형으로 변환할 수 있습니다.

(type_name) expression

Objective-C에서는 일반적으로 32비트의 경우 float의 기본 유형에서 파생된 부동 소수점 연산을 수행하기 위해 CGFloat를 사용하고 64비트의 경우 double을 사용합니다.

다음은 캐스트 연산자가 하나의 정수 변수를 다른 정수 변수로 나누는 것이 부동 소수점 연산으로 수행되도록 하는 예입니다.

#import <Foundation/Foundation.h>

int main() {
 int sum = 17, count = 5;
 CGFloat mean;
 
 mean = (CGFloat) sum / count;
 NSLog(@"Value of mean : %f\n", mean );
 
 return 0;
}

// Value of mean : 3.400000

여기서 캐스트 연산자가 나눗셈보다 우선하므로 sum 값 은 먼저 double 유형으로 변환 되고 마지막으로 count로 나누어 double 값을 생성한다는 점에 유의해야 합니다.

유형 변환은 컴파일러에 의해 자동으로 수행되는 암시적이거나 캐스트 연산자 를 사용하여 명시적으로 지정될 수 있습니다.

유형 변환이 필요할 때마다 캐스트 연산자를 사용하는 것은 좋은 프로그래밍 방법으로 간주됩니다.

정수로 끌어올리기(Integer Promotion)

정수 승격은 int 또는 unsigned int 보다 "작은" 정수 유형의 값이 int 또는 unsigned int로 변환 되는 프로세스 입니다.

다음은 int에 문자를 추가하는 예입니다.

#import <Foundation/Foundation.h>

int main() {
 int i = 17;
 char c = 'c'; /* ascii value is 99 */
 int sum;
 
 sum = i + c;
 NSLog(@"Value of sum : %d\n", sum );
 
 return 0;
}

// Value of sum : 116

여기서 sum 값은 실제 덧셈 연산을 수행하기 전에 컴파일러가 정수 승격을 하고 'c' 값을 ascii로 변환하기 때문에 116이 됩니다.

일반적인 산술 변환(Usual Arithmetic Conversion)

일반적인 산술 변환 은 해당 값을 공통 유형으로 캐스팅하기 위해 암시적으로 수행됩니다.

컴파일러는 먼저 정수 승격을 수행합니다.

피연산자의 유형이 여전히 다른 경우 다음 계층에서 가장 높은 유형으로 변환 됩니다.

할당 연산자나 논리 연산자 && 및 ||에 대해서는 일반적인 산술 변환이 수행되지 않습니다.

#import <Foundation/Foundation.h>

int main() {
 int i = 17;
 char c = 'c'; /* ascii value is 99 */
 CGFloat sum;
 
 sum = i + c;
 NSLog(@"Value of sum : %f\n", sum );
 
 return 0;
}

// Value of sum : 116.000000

여기서 첫 번째 c는 정수로 변환되지만 최종 값은 부동 소수점이기 때문에 일반적인 산술 변환이 적용되고 컴파일러는 i와 c를 부동 소수점으로 변환하고 더하여 부동 소수점 결과를 산출한다는 것을 이해하는 것은 간단합니다.

로그 처리

NSLog 메서드(NSLog method)

로그를 인쇄하기 위해 Hello World 예제에서 바로 사용한 Objective-C 프로그래밍 언어의 NSLog 메서드를 사용합니다.

"Hello World"라는 단어를 인쇄하는 간단한 코드를 살펴보겠습니다.

#import <Foundation/Foundation.h>

int main() {
 NSLog(@“Hello, World! \n”);
 
 return 0;
}

// Hello, World!

라이브 앱에서 로그 비활성화(Disabling logs in Live apps)

우리 앱에서 사용하는 NSLogs 때문에 디바이스의 로그에 출력되고 라이브 빌드에서 로그를 인쇄하는 것은 좋지 않습니다.

따라서 로그를 인쇄하기 위해 유형 정의를 사용하고 아래와 같이 사용할 수 있습니다.

#import <Foundation/Foundation.h>

#if DEBUG == 0
#define DebugLog(...)
#elif DEBUG == 1
#define DebugLog(...) NSLog(__VA_ARGS__)
#endif

int main() {
 DebugLog(@"Debug log, our custom addition gets \
 printed during debug only" );
 NSLog(@"NSLog gets printed always" );
 return 0;
}

/*
[DEBUG MODE]
Debug log, our custom addition gets printed during debug only
NSLog gets printed always

[RELEASE MODE]
NSLog gets printed always
*/

오류 처리(Error Handling)

Objective-C 프로그래밍에서 오류 처리는 Foundation 프레임워크에서 사용할 수 있는 NSError 클래스와 함께 제공됩니다.

NSError 개체는 오류 코드 또는 오류 문자열만 사용하여 가능한 것보다 더 풍부하고 확장 가능한 오류 정보를 캡슐화합니다.

NSError 객체의 핵심 속성은 오류 도메인(문자열로 표시), 도메인 특정 오류 코드 및 응용 프로그램 특정 정보를 포함하는 사용자 정보 사전입니다.

NSError

Objective-C 프로그램은 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];
#import <Foundation/Foundation.h>

@interface SampleClass:NSObject
-(NSString *) getEmployeeNameForID:(int) id withError:(NSError
**)errorPtr;
@end

@implementation SampleClass

-(NSString *) getEmployeeNameForID:(int) id withError:(NSError
**)errorPtr {
 if(id == 1) {
 return @"Employee Test Name";
 } else {
 NSString *domain =
@"com.MyCompany.MyApplication.ErrorDomain";
 NSString *desc =@"Unable to complete the process";
 NSDictionary *userInfo = [[NSDictionary alloc]
 initWithObjectsAndKeys:desc,
 @"NSLocalizedDescriptionKey",NULL];
 *errorPtr = [NSError errorWithDomain:domain code:-101
 userInfo:userInfo];
 return @"";
 }
}

@end

int main() {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 SampleClass *sampleClass = [[SampleClass alloc]init];
 NSError *error = nil;
 NSString *name1 = [sampleClass getEmployeeNameForID:1 withError:&error];

 if(error) {
 NSLog(@"Error finding Name1: %@",error);
 } else {
 NSLog(@"Name1: %@",name1);
 }

 error = nil;
 NSString *name2 = [sampleClass getEmployeeNameForID:2 withError:&error];
 
 if(error) {
 NSLog(@"Error finding Name2: %@",error);
 } else {
 NSLog(@"Name2: %@",name2);
 }
 
 [pool drain];
 return 0;
}

// Name1: Employee Test Name
// Error finding Name2: Unable to complete the process

클래스와 객체(Classes & Objects)

Objective-C 프로그래밍 언어의 주요 목적은 C 프로그래밍 언어에 객체 지향을 추가하는 것이며 클래스는 객체 지향 프로그래밍을 지원하고 종종 사용자 정의 유형이라고 불리는 Objective-C의 핵심 기능입니다.

클래스는 개체의 형식을 지정하는 데 사용되며 데이터 표현과 해당 데이터를 하나의 깔끔한 패키지로 조작하는 방법을 결합합니다. 클래스 내의 데이터와 메소드를 클래스의 멤버라고 합니다.

  • 클래스는 @interface 및 @implementation 이라는 두 개의 다른 섹션에서 정의됩니다.

  • 거의 모든 것이 객체의 형태입니다.

  • 객체는 메시지를 수신하고 객체는 종종 수신자라고 합니다. (메소드를 호출한다)

  • 객체는 인스턴스 변수를 포함합니다.

  • 개체 및 인스턴스 변수에는 범위가 있습니다.

  • 클래스는 개체의 구현을 숨깁니다.

  • 속성은 다른 클래스의 클래스 인스턴스 변수에 대한 액세스를 제공하는 데 사용됩니다.

클래스 정의(Class Definitions)

클래스를 정의할 때 데이터 유형에 대한 청사진을 정의합니다.

이것은 실제로 데이터를 정의하지 않지만 클래스 이름이 의미하는 것, 즉 클래스의 개체가 무엇으로 구성되고 이러한 개체에서 수행할 수 있는 작업을 정의합니다.

클래스 정의는 @interface 키워드와 인터페이스(클래스) 이름으로 시작합니다. 한 쌍의 중괄호로 묶인 클래스 본문. Objective-C에서 모든 클래스는
NSObject 라는 기본 클래스에서 파생됩니다.

모든 Objective-C 클래스의 상위 클래스입니다. 메모리 할당 및 초기화와 같은 기본 방법을 제공합니다.

예를 들어 다음과 같이 키워드 class 를 사용하여 Box 데이터 유형을 정의했습니다.

인스턴스 변수는 비공개이며 클래스 구현 내에서만 액세스할 수 있습니다.

#import <Foundation/Foundation.h>

@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)

클래스는 객체에 대한 청사진을 제공하므로 기본적으로 객체는 클래스에서 생성됩니다.

기본 유형의 변수를 선언하는 것과 정확히 같은 종류의 선언으로 클래스의 개체를 선언합니다.

다음 명령문은 Box 클래스의 두 객체를 선언합니다.

box1 및 box2 개체 모두 데이터 멤버의 고유한 복사본을 갖습니다.

Box box1 = [[Box alloc]init]; // Create box1 object of type Box
Box box2 = [[Box alloc]init]; // Create box2 object of type Box

클래스의 개체 속성은 직접 멤버 액세스 연산자(.)를 사용하여 액세스할 수 있습니다.

데이터 멤버 액세스(Accessing the Data Members)

#import <Foundation/Foundation.h>

@interface Box:NSObject {
 double length; // Length of a box
 double breadth; // Breadth of a box
 double height; // Height of a box
}

@property(nonatomic, readwrite) double height; // Property
-(double) volume;
@end

@implementation Box

@synthesize height;

-(id)init {
 self = [super init];
 length = 1.0;
 breadth = 1.0;
 return self;
}

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

int main() {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 Box *box1 = [[Box alloc]init]; // Create box1 object of type Box
 Box *box2 = [[Box alloc]init]; // Create box2 object of type Box
 
 double volume = 0.0; // Store the volume of a box here

 // box 1 specification
 box1.height = 5.0;
 
 // box 2 specification
 box2.height = 10.0;

 // volume of box 1
 volume = [box1 volume];
 NSLog(@"Volume of Box1 : %f", volume);

 // volume of box 2
 volume = [box2 volume];
 NSLog(@"Volume of Box2 : %f", volume);

 [pool drain];
 return 0;
}

// Volume of Box1 : 5.000000
// Volume of Box2 : 10.000000

속성(Properties)

속성은 클래스 외부에서 클래스의 인스턴스 변수에 액세스할 수 있도록 하기 위해 Objective-C에 도입되었습니다.

속성 은 키워드인 @property 로 시작합니다.

그 뒤에는 비원자 또는 원자, readwrite 또는 readonly 및 strong, unsafe_unretained 또는 weak인 액세스 지정자가 옵니다. 이것은 변수의 유형에 따라 다릅니다. 모든 포인터 유형에 대해 strong, unsafe_unretained 또는 weak를 사용할 수 있습니다. 마찬가지로 다른 유형의 경우 readwrite 또는 readonly를 사용할 수 있습니다.

그 다음에 변수의 데이터 유형이 옵니다.

마지막으로 세미콜론으로 끝나는 속성 이름이 있습니다.

구현 클래스에 synthesize 문을 추가할 수 있습니다. 그러나 최신 XCode에서는 합성 부분을 XCode에서 처리하므로 synthesize 문을 포함할 필요가 없습니다.

클래스의 인스턴스 변수에 액세스할 수 있는 속성에서만 가능합니다. 실제로 속성에 대해 내부적으로 getter 및 setter 메서드가 생성됩니다.

예를 들어, @property (nonatomic,readonly) BOOL isDone 속성이 있다고 가정해 보겠습니다 . 이 예제에는 아래와 같이 생성된 setter와 getter가 있습니다.

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

상속(Inheritance)

객체 지향 프로그래밍에서 가장 중요한 개념 중 하나는 상속입니다. 상속을 통해 다른 클래스로 클래스를 정의할 수 있으므로 애플리케이션을 더 쉽게 만들고 유지 관리할 수 있습니다. 이것은 또한 코드 기능과 빠른 구현 시간을 재사용할 수 있는 기회를 제공합니다.

클래스를 생성할 때 완전히 새로운 데이터 멤버와 멤버 함수를 작성하는 대신 프로그래머는 새 클래스가 기존 클래스의 멤버를 상속하도록 지정할 수 있습니다. 이 기존 클래스를 기본 클래스라고 하고 새 클래스를 파생 클래스라고 합니다.

상속을 구현한다는 아이디어는 관계 입니다. 예를 들어, 포유류 IS-A 동물, 개 IS-A 포유류, 따라서 개 IS-A 동물 등입니다.

기본 및 파생 클래스(Base & Derived Classes)

Objective-C는 다단계 상속만 허용합니다. 즉, 기본 클래스는 하나만 가질 수 있지만 다단계 상속은 허용합니다.

Objective-C의 모든 클래스는 NSObject 수퍼클래스에서 파생됩니다.

@interface derived-class: base-class

다음과 같이 기본 클래스 Person과 파생 클래스 Employee를 생각해봅시다.

데이터 멤버 액세스(Accessing the Data Members)

#import <Foundation/Foundation.h>

@interface Person : NSObject {
 NSString *personName;
 NSInteger personAge;
}

- (id)initWithName:(NSString *)name andAge:(NSInteger)age;
- (void)print;

@end

@implementation Person

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

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

@interface Employee : Person {
 NSString *employeeEducation;
}

- (id)initWithName:(NSString *)name andAge:(NSInteger)age
 andEducation:(NSString *)education;
- (void)print;
@end

@implementation Employee

- (id)initWithName:(NSString *)name andAge:(NSInteger)age
 andEducation: (NSString *)education {
 personName = name;
 personAge = age;
 employeeEducation = education;
 return self;
 }
 
- (void)print {
 NSLog(@"Name: %@", personName);
 NSLog(@"Age: %ld", personAge);
 NSLog(@"Education: %@", employeeEducation);
}
@end

int main(int argc, const char * argv[]) {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 NSLog(@"Base class Person Object");
 Person *person = [[Person alloc]initWithName:@"Raj" andAge:5];
 [person print];
 NSLog(@"Inherited Class Employee Object");
 Employee *employee = [[Employee alloc]initWithName:@"Raj"
 andAge:5 andEducation:@"MBA"];
 [employee print];
 [pool drain];
 return 0;
}

/*
Base class Person Object
Name: Raj
Age: 5
Inherited Class Employee Object
Name: Raj
Age: 5
Education: MBA
*/

액세스 제어 및 상속(Access Control and Inheritance)

파생 클래스는 인터페이스 클래스에 정의된 경우 기본 클래스의 모든 private 멤버에 액세스할 수 있지만 구현 파일에 정의된 private 멤버에는 액세스할 수 없습니다.

다음과 같은 방식으로 액세스할 수 있는 사람에 따라 다양한 액세스 유형을 요약할 수 있습니다.

파생 클래스는 다음 예외를 제외하고 모든 기본 클래스 메서드와 변수를 상속합니다.

  • 확장을 사용하여 구현 파일에 선언된 변수에 액세스할 수 없습니다.

  • 확장을 사용하여 구현 파일에 선언된 메서드에 액세스할 수 없습니다.

  • 상속된 클래스가 기본 클래스의 메서드를 구현하는 경우 파생 클래스의 메서드가 실행됩니다.

다형성(Polymorphism)

다형성 이라는 단어 는 많은 형태를 갖는다는 의미입니다. 일반적으로 다형성은 클래스의 계층 구조가 있고 상속으로 관련되어 있을 때 발생합니다.

Objective-C 다형성은 멤버 함수에 대한 호출이 함수를 호출하는 객체의 유형에 따라 다른 함수가 실행되도록 한다는 것을 의미합니다.

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

#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 {
 length = side;
 return self;
}

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

- (void)printArea {
 NSLog(@"The area of square is %f", area);
}
@end

@interface Rectangle : Shape {
 CGFloat length;
 CGFloat breadth;
}

- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth;
@end

@implementation Rectangle
- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth {
 length = rLength;
 breadth = rBreadth;
 return self;
}

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

int main(int argc, const char * argv[]) {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
 Shape *square = [[Square alloc]initWithSide:10.0];
 [square calculateArea];
 [square printArea];
 
 Shape *rect = [[Rectangle alloc]
 initWithLength:10.0 andBreadth:5.0];
 [rect calculateArea];
 [rect printArea];

 [pool drain];
 return 0;
}

// The area of square is 100.000000
// The area is 50.000000

위의 예제에서는 computeArea 및 printArea 메서드의 가용성을 기반으로 기본 클래스 또는 파생 클래스의 메서드가 실행되었습니다.

다형성은 두 클래스의 메서드 구현을 기반으로 기본 클래스와 파생 클래스 간의 메서드 전환을 처리합니다.

데이터 캡슐화(Data Encapsulation)

모든 Objective-C 프로그램은 다음 두 가지 기본 요소로 구성됩니다.

  • 프로그램 문(코드) - 이것은 작업을 수행하는 프로그램의 일부이며 이를 메서드라고 합니다.

  • 프로그램 데이터 - 데이터는 프로그램 기능의 영향을 받는 프로그램 정보입니다.

캡슐화는 데이터와 데이터를 조작하는 기능을 함께 묶고 외부 간섭과 오용으로부터 안전하게 유지하는 객체 지향 프로그래밍 개념입니다.

데이터 캡슐화는 데이터 은닉 이라는 중요한 OOP 개념으로 이어졌습니다.

데이터 캡슐화 는 데이터와 이를 사용하는 기능을 묶는 메커니즘이고 데이터 추상화 는 인터페이스만 노출하고 구현 세부 사항을 사용자에게 숨기는 메커니즘입니다.

Objective-C는 클래스라고 하는 사용자 정의 유형의 생성을 통해 캡슐화 및 데이터 은닉의 속성을 지원합니다.

@interface Adder : NSObject {
 NSInteger total;
}

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

@end

변수 total은 비공개이며 클래스 외부에서 액세스할 수 없습니다. 이것은 Adder 클래스의 다른 멤버만 액세스할 수 있으며 프로그램의 다른 부분에서는 액세스할 수 없음을 의미합니다. 이것은 캡슐화를 달성하는 한 가지 방법입니다.

인터페이스 파일 내의 메서드는 액세스할 수 있으며 범위 내에서 공개됩니다.

밑에서 배우게 될 확장 기능(extension)도 참고하세요.

데이터 캡슐화 예(Data Encapsulation Example)

public 및 private 멤버 변수를 사용하여 클래스를 구현하는 모든 Objective-C 프로그램은 데이터 캡슐화 및 데이터 추상화의 예입니다.

#import <Foundation/Foundation.h>

@interface Adder : NSObject {
 NSInteger total;
}

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

@implementation Adder
-(id)initWithInitialNumber:(NSInteger)initialNumber {
 total = initialNumber;
 return self;
}

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

- (NSInteger)getTotal {
 return total;
}
@end

int main(int argc, const char * argv[]) {
 NSAutoreleasePool * pool
= [[NSAutoreleasePool alloc] init];

 Adder *adder = [[Adder alloc]initWithInitialNumber:10];
 [adder addNumber:5];
 [adder addNumber:4];

 NSLog(@"The total is %ld",[adder getTotal]);
 
 [pool drain];
 return 0;
}

// The total is 19

위의 클래스는 숫자를 더하고 합계를 반환합니다.

public 멤버인 addNum 및 getTotal 은 외부 세계에 대한 인터페이스이며 사용자는 클래스를 사용하기 위해 이를 알아야 합니다.

private 멤버 total 은 외부 세계에 숨겨져 있지만 클래스가 제대로 작동하기 위해 필요한 것입니다.

전략 설계(Designing Strategy)

우리 대부분은 실제로 노출해야 하는 경우가 아니면 기본적으로 클래스 멤버를 비공개로 설정하는 쓰라린 경험을 통해 배웠습니다. 이것이 좋은 캡슐화 입니다.

데이터 캡슐화는 Objective-C를 포함한 모든 객체 지향 프로그래밍(OOP) 언어의 핵심 기능 중 하나이기 때문에 데이터 캡슐화를 이해하는 것이 중요합니다.

카테고리(Categories)

때로는 특정 상황에서만 유용한 동작을 추가하여 기존 클래스를 확장하려는 경우가 있습니다. 이러한 확장을 기존 클래스에 추가하기 위해 Objective-C는 범주(카테고리) 및 확장(익스텐션) 을 제공합니다.

기존 클래스에 메서드를 추가해야 하는 경우, 아마도 자신의 애플리케이션에서 더 쉽게 수행할 수 있도록 기능을 추가해야 하는 경우 가장 쉬운 방법은 카테고리를 사용하는 것입니다.

카테고리를 선언하는 구문은 표준 Objective-C 클래스 설명과 마찬가지로 @interface 키워드를 사용하지만 하위 클래스로부터의 상속을 나타내지는 않습니다. 대신, 다음과 같이 괄호 안에 카테고리 이름을 지정합니다.

@interface ClassName (CategoryName)

@end

카테고리의 특성(Characteristics of Category)

원래 구현 소스 코드가 없더라도 모든 클래스에 대해 카테고리를 선언할 수 있습니다.

카테고리에서 선언하는 모든 메서드는 원래 클래스의 모든 하위 클래스뿐만 아니라 원래 클래스의 모든 인스턴스에서 사용할 수 있습니다.

런타임에는 카테고리에 의해 추가된 메서드와 원래 클래스에 의해 구현되는 메서드 사이에 차이가 없습니다.

Cocoa 클래스 NSString에 카테고리를 추가해 보겠습니다.

이 카테고리를 사용하면 저작권 문자열을 반환하는 데 도움이 되는 새 메서드 getCopyRightString을 추가할 수 있습니다.

#import <Foundation/Foundation.h>

@interface NSString(MyAdditions)
+(NSString *)getCopyRightString;
@end

@implementation NSString(MyAdditions)
+(NSString *)getCopyRightString {
 return @"Copyright TutorialsPoint.com 2013";
}
@end

int main(int argc, const char * argv[]) {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 NSString *copyrightString = [NSString getCopyRightString];
 NSLog(@"Accessing Category: %@",copyrightString);

 [pool drain];
 return 0;
}

// Accessing Category: Copyright TutorialsPoint.com 2013

카테고리에 의해 추가된 메서드는 클래스 및 해당 하위 클래스의 모든 인스턴스에서 사용할 수 있지만 추가 메서드를 사용하려는 소스 코드 파일에서 범주 헤더 파일을 가져와야 합니다. 그렇지 않으면 컴파일러 경고 및 오류.

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

확장(Extensions)

클래스 확장은 카테고리와 어느 정도 유사하지만 컴파일 시간에 소스 코드가 있는 클래스에만 추가할 수 있습니다(클래스는 클래스 확장과 동시에 컴파일됨).

클래스 확장으로 선언된 메서드는 원래 클래스의 구현 블록에서 구현되므로 예를 들어 NSString과 같은 Cocoa 또는 Cocoa Touch 클래스와 같은 프레임워크 클래스에서 클래스 확장을 선언할 수 없습니다.

확장은 실제로 범주 이름이 없는 범주입니다. 종종 익명 카테고리 라고 합니다.

확장을 선언하는 구문은 표준 Objective-C 클래스 설명과 마찬가지로 @interface 키워드를 사용하지만 하위 클래스로부터의 상속을 나타내지는 않습니다. 대신 아래와 같이 괄호만 추가합니다.

@interface ClassName()

@end

확장의 특성(Characteristics of Extensions)

확장은 모든 클래스에 대해 선언할 수 없으며 소스 코드의 원래 구현이 있는 클래스에만 적용됩니다.

확장은 해당 클래스에만 해당하는 개인 메서드와 개인 변수를 추가하는 것입니다.

확장 내부에 선언된 메서드나 변수는 상속된 클래스에서도 액세스할 수 없습니다.

확장 예(Extensions Example)

확장이 있는 SampleClass 클래스를 생성해 보겠습니다. 확장에 private 변수 internalID가 있습니다.

그럼 내부ID를 처리한 후 외부ID를 반환하는 getExternalID 메소드를 만들어보자.

예제는 아래에 나와 있으며 온라인 컴파일러에서는 작동하지 않습니다.

#import <Foundation/Foundation.h>

@interface SampleClass : NSObject {
 NSString *name;
}

- (void)setInternalID;
- (NSString *)getExternalID;

@end

@interface SampleClass() {
 NSString *internalID;
}
@end

@implementation SampleClass

- (void)setInternalID {
 internalID = [NSString stringWithFormat:
@"UNIQUEINTERNALKEY%dUNIQUEINTERNALKEY",arc4random()%100];
}

- (NSString *)getExternalID {
 return [internalID stringByReplacingOccurrencesOfString:
 @"UNIQUEINTERNALKEY" withString:@""];
}

@end

int main(int argc, const char * argv[]) {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 SampleClass *sampleClass = [[SampleClass alloc]init];
 [sampleClass setInternalID];
 NSLog(@"ExternalID: %@",[sampleClass getExternalID]);
 [pool drain];
 return 0;
}

// ExternalID: 51

위의 예에서 internalID가 직접 반환되지 않는 것을 볼 수 있습니다. 여기서 UNIQUEINTERNALKEY를 제거하고 나머지 값만 getExternalID 메서드에서 사용할 수 있도록 합니다.

위의 예는 문자열 연산만을 사용하지만 암호화/복호화 등 많은 기능을 가질 수 있습니다.

프로토콜(Protocols)

Objective-C를 사용하면 특정 상황에 사용될 것으로 예상되는 메서드를 선언하는 프로토콜을 정의할 수 있습니다. 프로토콜은 프로토콜을 준수하는 클래스에서 구현됩니다.

간단한 예는 네트워크 URL 처리 클래스이며, 네트워크 URL 가져오기 작업이 끝나면 호출 클래스에 친밀감을 주는 processCompleted 대리자 메서드와 같은 메서드가 있는 프로토콜이 있습니다.

프로토콜의 구문은 다음과 같습니다.

@protocol ProtocolName
@required
// list of required methods
@optional
// list of optional methods
@end 

@required 키워드 아래의 메서드는 프로토콜을 준수하는 클래스에서 구현되어야 하며 @optional 키워드 아래의 메서드는 선택적으로 구현해야 합니다.

다음은 프로토콜을 준수하는 클래스의 구문입니다.

@interface MyClass : NSObject <MyProtocol>
…
@end

이것은 MyClass의 모든 인스턴스가 인터페이스에서 구체적으로 선언된 메소드에 응답할 뿐만 아니라 MyClass가 MyProtocol의 필수 메소드에 대한 구현도 제공한다는 것을 의미합니다.

클래스 인터페이스에서 프로토콜 메서드를 다시 선언할 필요가 없습니다. 프로토콜을 채택하면 충분합니다.

여러 프로토콜을 채택하는 클래스가 필요한 경우 쉼표로 구분된 목록으로 지정할 수 있습니다.

프로토콜을 구현하는 호출 개체의 참조를 보유하는 대리자 개체가 있습니다.

#import <Foundation/Foundation.h>

@protocol PrintProtocolDelegate
- (void)processCompleted;

@end

@interface PrintClass :NSObject {
 id delegate;
}

- (void) printDetails;
- (void) setDelegate:(id)newDelegate;
@end

@implementation PrintClass
- (void)printDetails {
 NSLog(@"Printing Details");
 [delegate processCompleted];
}

- (void) setDelegate:(id)newDelegate {
 delegate = newDelegate;
}
@end

@interface SampleClass:NSObject<PrintProtocolDelegate>
- (void)startAction;

@end

@implementation SampleClass
- (void)startAction {
 PrintClass *printClass = [[PrintClass alloc]init];
 [printClass setDelegate:self];
 [printClass printDetails];
}

-(void)processCompleted {
 NSLog(@"Printing Process Completed");
}

@end

int main(int argc, const char * argv[]) {
 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 SampleClass *sampleClass = [[SampleClass alloc]init];
 [sampleClass startAction];
 [pool drain];
 return 0;
}

// Printing Details
// Printing Process Completed

앞의 예에서 우리는 delgate 메소드가 어떻게 호출되고 실행되는지 보았습니다.

startAction으로 시작하여 프로세스가 완료되면 대리자 메서드인 processCompleted가 호출되어 작업이 완료되었음을 알립니다.

모든 iOS 또는 Mac 앱에서는 대리인 없이 프로그램을 구현하지 않습니다.

그래서 우리는 대리자의 사용법을 이해하는 것이 중요합니다.

대리자 개체는 메모리 누수를 방지하기 위해 unsafe_unretained 속성 유형을 사용해야 합니다.

클래스 메소드 초기화에서 보이는 id의 정체

객체를 가리키기 위한 포인터를 선언할 때는 대개 다음과 같이 해당 객체의 클래스를 지정한다.

NSDate *expiration;

하지만 정확히 어떤 객체를 가리킬지 특정하지 않고 포인터만을 만들어야 할 때도 적지 않다.

이런 경우 id라는 타입을 사용하는데, id의 의미는 "어떤 Objective-C 객체를 가리키는 포인터"다.

id delegate;

선언문에 에스터리스크( * )가 없다는 점에 유의해야 한다. id가 애스터리스크를 내포한다.

0개의 댓글