DADAHAE's Log
비싼 장난감 가지고 노는 중 (❁´▽`❁)*✲゚*
[TIL] 22-10-12: Objective-C (Protocol, ARC, binding)
22-10-12 수요일
5주차

 

 

 

 

 

 

 

 

WARNING
⚠️ 경고 ⚠️

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

✔️ Objective-C

 

 

 


 

 

 

♻️ 객체, 카테고리, 익스텐션 그리고 프로토콜

다시보기

 

objc에서는 카테고리만 알면 된다. 익스텐션은 많이 쓰이지도 않고…

  • 객체의 id
    • Object type
    • *** 이 포함된, 특정할 수 없는 객체 타입**

 

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

id
  • 포인터를 선언할 때 아래와 같이 객체의 클래스를 지정함
NSDate *expiration;

 

  • But… 정확히 어떤 객체인지 특정하지 않고 포인터만 만들어야 하는 경우도 있다.
  • 이런 경우 id 타입 사용, 어떤 Objective-C 객체를 가리키는 포인터
id delegate;
  • 굳이 *을 붙이지 않음. id는 *의미를 내포한다.

 

Protocols

  • 특정 상황에 사용될 것으로 예상되는 메서드를 선언하는 프로토콜을 정의

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

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

프로토콜 사용 예시 코드 + id

#import <Foundation/Foundation.h>

// Objective-C 에서는 이런 일을 해줄 클래스를 찾는다면서 Delegate라고 표현한다.
// (Delegate: 위임자, 대행자)
@protocol PrintProtocolDelegate
@required // 이것은 필수입니다. (Objective-C 에서 습관적으로 써주는게 좋다)
- (void)processCompleted;

@end

@interface PrintClass : NSObject {
    // 내가 위임자로서 그 일을 하겠다는 데가 있으면 그게 바로 delegate
    // id는 "어떤 Objective-C 객체를 가리키는 포인터"
    id delegate;
}

// 이 클래스가 원하는 일을 하는 대신 해줄 delegate 객체를 지정하는 메서드
- (void)setDelegate:(id)newDelegate;
- (void)printDetails;
@end

@implementation PrintClass

- (void)setDelegate:(id)newDelegate {
    delegate = newDelegate;
}
- (void)printDetails {
    NSLog(@"Print Details");
    // delegate 객체가 PrintProtocolDelegate 프로토콜을 잘 따른다면
    // processCompleted 메서드가 있기 때문에 호출 가능하다.
    // @required가 아니라 @optional 이면 확인이 필요하다.
    // if ([delegate performSelec..])
    // 더 확실히 하기위해 해당 메서드가 실행가능한지 체크해보는 습관이 좋다.
    if ([delegate respondsToSelector:@selector(processCompleted)]) {
//        [delegate processCompleted];
        [delegate performSelector:@selector(processCompleted)];
    }
}

@end

// 이런 프로토콜을 따르냐! 보다는 이런 delegate가 되어줄 수 있나요? 를 더 많이 씀
@interface SampleClass : NSObject<PrintProtocolDelegate>
- (void)startAction;
@end

@implementation SampleClass
- (void)startAction {
    PrintClass *printClass = [[PrintClass alloc] init];
    
    // PrintClass 클래스로 만들어진 printClass 인스턴스 객체에게
    // 내가 너의 delegate가 되어줄게! 라고 말해준다.
    // PrintProtocolDelegate 프로토콜을 따라주니까
    // PrintClass 클래스에서 요구하는 delegate 역할을 잘 해줄거라 기대한다.
    [printClass setDelegate:self];
    
    [printClass printDetails];
}
- (void)processCompleted {
    NSLog(@"Printing Process Completed");
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SampleClass *sampleClass = [[SampleClass alloc] init];
        [sampleClass startAction];
    }
    return 0;
}
  • 모든 iOS 또는 Mac 앱에서 delegate 없이 프로그램을 구현하지 않음.
    • so… delegate 사용법을 이해하는 것이 중요함!
    • 화면상에 표, 테이블, 게시판 쓸 때
  • delegate 객체는 메모리 누수를 방지하기 위해 unsafe_unretained 속성 유형을 사용해야 함 (요 부분 아직 몰라두 된다)
  • 요즘 swift 에서는 특정 인스턴스의 메서드에 함수나 클로저를 직접 매개변수로 담아서 일을 시킬 수 있지만 옛날에는 클로저 같은 수단이 없었을 때(블록도 나오기 전에는) 프로토콜을 따르는 인스턴스들을 만들어서 델리게이트에게 그런 일 해줄 완전한 대행자를 세워놓고 일 시키는 꼴이었다

 

메소드에서 self가 가지는 의미

self
  • 어떤 메소드 안에서도 묵시적 지역 변수인 self에 접근할 수 있다.
  • self는 메소드의 실행 주체가 되는 객체의 포인터이다.
    • 객체가 자신에게 메시지를 보내야할 때 self가 사용된다.
#import <Foundation/Foundation.h>

// BMI 계산

@interface Person : NSObject {
    float heightInMeter;
    int weightInKilos;
}
@property(nonatomic, readwrite) float heightInMeter;
@property(nonatomic, readwrite) int weightInKilos;

- (float)bodyMassIndex;
@end

@implementation Person

@synthesize heightInMeter;
@synthesize weightInKilos;

- (float)bodyMassIndex {
    // self는 메소드의 실행 주체가 되는 객체의 포인터다.
    // 객체가 자신에게 메시지를 보내야할 때 이 self가 사용된다.
    float height = [self heightInMeter];   // property가 만든 getter 메소드
    return [self weightInKilos] / ( height * height);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        person.heightInMeter = 1.5;
        person.weightInKilos = 50;
        NSLog(@"%f", [person bodyMassIndex]);
    }
    return 0;
}

나야나 너니까 실행해줄게

  • self를 인수로 전달하면 현재 객체가 어디있는지 다른 객체들이 알 수 있다.
    • self가 매개변수의 대상이 될 수 있다.
- (void)add

 

Categories

  • category를 사용하여 기존의 클래스에 메소드를 추가할 수 있다.

ex) NSString 클래스를 제공하고 우리는 그 구현 코드를 알 수 없지만, category를 통해 NSString에 필요한 새로운 메서드를 추가할 수 있다.

  • category가 담겨있는 파일 이름은
    • NSString + 카테고리이름.h 와 .m 파일로 만드는게 보통.
#import <Foundation/Foundation.h>

// NSString에 기능을 추가해보자.
// 문자열에서 모음 문자의 갯수를 알아보려는 카테고리
@interface NSString(VowelCounting)
- (int)vowelCount;
@end

@implementation NSString(VowelCounting)

- (int)vowelCount {
    NSCharacterSet *charSet = [NSCharacterSet characterSetWithCharactersInString:@"aeiouyAEIOUY"];
    NSInteger count = [self length]; // getter method
    
    int sum = 0;
    int index = 0;
    
    for (index = 0; index < count; index++) {
        unichar character = [self characterAtIndex:index];
        if ([charSet characterIsMember:character]) {
            sum++;
        }
    }
    
    return sum;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *string = @"Hello World";
        NSLog(@"%@ has %d vowels.", string, [string vowelCount]);
    }
    return 0;
}

 

  • 카테고리는 원래 클래스의 인스턴스에 접근o, 새 인스턴스 변수 추가x
    • 인스턴수 변수를 추가하고 싶다면 ‘상속받은 서브클래스’를 만들어야 함
  • 카테고리는 원래 있던 메서드 오버라이드o, 그러나 나쁜 프로그래밍 습관
    • 서브 클래스의 오버라이드된 메서드로 만드는 것이 좋다.

 

 

 


 

 

♻️ What is delegate?

delegate

요즘 swift 에서는 특정 인스턴스의 메서드에 함수나 클로저를 직접 매개변수로 담아서 일을 시킬 수 있지만 옛날에는 클로저 같은 수단이 없었을 때(블록도 나오기 전에는) 프로토콜을 따르는 인스턴스들을 만들어서 델리게이트에게 그런 일 해줄 완전한 대행자를 세워놓고 일 시키는 꼴이었다.

 

 

 

 


1️⃣ 동적 바인딩

var counter = myCounter // func
counter = otherCounter
//?>>>??

 

Dynamic Binding

  • 컴파일 시간x, 런타임에 호출할 메서드 결정o
    • 실행 중에 역할을 바꿀 수 있다.

 

  • objc에서 모든 메소드는 런타임에 동적으로 해결된다 (코드로 이해해보자)
  • 동적 바인딩이 없다면? C++, objc의 차이가 그닥 없을 것이다.
    • C++
      • 다중 상속
      • 미리 complier가 정해버림
    • Obj-C
      • 단일 상속(C#, Swift, Java 등도 지원. 최신 언어는 거의 다 요거 따름)
      • Dynamic Binding

 

  • 다형성 가능
    • 실행하는 도중에 어떤 메소드를 실행시킬지 결정한다!
// 정사각형 정보를 담은 클래스
@interface Square : NSObject {
    float area; // 면적
}
- (void)calculateAreaOfSide:(CGFloat)side;  // 네 변의 길이가 같으므로 하나의 길이만 가져와도 계산 가능
- (void)printArea; // 계산된 면적을 출력하는 메서드
@end

@implementation Square

- (void)calculateAreaOfSide:(CGFloat)side {
    area = side * side;
}

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

@end

// 가로 세로 길이가 별개의 일반적인 사각형
@interface Rectangle: NSObject{
    float area; // 면적
}
- (void)calculateAreaOfLength:(CGFloat)lenght andBreadth:(CGFloat)breadth;
- (void)printArea; // 계산된 면적을 출력하는 메서드
@end

@implementation Rectangle

- (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth {
    area = length * breadth;
}
- (void)printArea {
    NSLog(@"The area of rectangle is %f.", area);
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Square *square = [[Square alloc] init];
        [square calculateAreaOfSide:10.0];
//        [square printArea];
        
        Rectangle *rectangle = [[Rectangle alloc] init];
        [rectangle calculateAreaOfLength:10.0 andBreadth:5.0];
//        [rectangle printArea];
        
        // [object printArea] 메소드 실행은,
        // 매번 object에 printArea 메소드가 각각 무엇인지 확인하고
        // 해당 메소드로 실행해서, 서로 다른 결과가 나올 수 있다.
        // 실행하는 도중에 어떤 메소드를 실행시킬지 결정한다!
        id object = square;
        [object printArea]; // The area of square is 100.000000.
        
        object = rectangle;
        [object printArea]; // The area of rectangle is 50.000000.
        
        object = square;
        [object printArea]; // The area of square is 100.000000.
    }
    return 0;
}
  • printArea 메서드는 런타임 시간에 동적으로 선택된다.
    • 같은 문장인데도 무엇이 실행되는지 모른다. 다르게 실행된다!
  • 이는 동적 바인딩의 예시이며, 유사한 종류의 객체를 처리할 때 많은 상황에서 유용하게 쓰인다.
더보기

Q. 실무에서 쓰이는 동적 바인딩의 예시가 있을까요? 메커니즘이 이해는 되는데, 어떤 상황에서 쓰이는지 궁금합니다.

 

A. 코드를 짜는 사람 마음이긴 한데, 나중에 블록 개념으로 이 함수 실행해줘 하지 이 함수는 이케 이케 하는건 좀 복잡해서…

클로저로 넘어가서 진행하고 있기 때문에 Swift에서는 크게 사용되고 있지는 않음.

물론 안쓰는 건 아닌데, 요즘엔 클로저로 대체되고 있다고 보면 좋다.

 

더보기

id

 

특정 클래스를 대놓고 쓰는게 좋고 안전하다. 근데 쓰는 경우가 있다는 것!

무조건 쓰이는 곳이 있다. 예를들어 init…

 

 

 

2️⃣ Composite Objects [PASS]

복합 객체
  • 클래스 클러스터내에 객체를 포함하는 클래스를 정의하는 하위 클래스 생성 가능
    • 이러한 클래스는 복합 개체이다…

 

Class Clusters

  • Foundation 프레임워크에서 광범위하게 사용하는 디자인 패턴
  • 공개 추상 슈퍼클래스 아래에 여러 개인 구체적인 하위 클래스를 그룹화한다.
  • Abstract Factory 디자인 패턴을 기반으로 함
  • 간단하게 하기 위해 유사한 기능에 대해 여러 클래스를 만드는 대신 입력값을 기반으로 처리하는 단일 클래스를 만든다.

예) NSNumber, char, int, bool 등과 같은 클래스 클러스터가 많이 있다.

 

 

What is a Composite Object?

그렇다면 복합 객체란 정확이 무엇일까용. 몰라요

??

넘어가자~

 

 

3️⃣ Foundation Framework

foundation이 도대체 뭔데 임포트해서 쓰는거냐~

  • Foundation 프레임워크는 objc 클래스의 기본 계층을 정의함
    • 작은 기본 유틸리티 클래스 세트 제공
  • NextStep에서 만들기 시작해서 NS 접두어 볼 수 있음
  • NSObject: 기초 키트 클래스를 포함한 모든 개체의 기본 클래스
  • 메모리 관리 방법 제공
  • 기본 클래스가 없으며, 모든 클래스의 기본이다.

 

 

Foundation classes based on functionality

  • 데이터 저장소
  • 텍스트, 문자열
  • 날짜, 시간
  • 예외처리
  • 파일 처리
  • URL loading system

 

Data storage

  • 데이터 저장 및 검색은 모든 프로그램에서 가장 중요한 것 중 하나이다.
  • Objc에서 일반적으로 작업을 복잡하게 만들기 때문에 linked-list와 같은 구조에 의존하지 않음
    • 대신 NSArray, NSSet, NSDictionary 및 변경 가능한(Mutable) 형식과 같은 컬렉션을 사용함.

 

NSArray와 NSMutableArray

  • NSMutableArrayNSArray에서 상속된다.
  • NSMutableArray 주요 메소드
    • removeAllObjects
    • addObjects
    • removeObjectAtIndex
    • exchangeObjectAtIndex:withObjectAtIndex
    • replaceObjectAtIndex:withObject

 

NSDictionary와 NSMutableDictionary

  • NSDictionary: 객체의 변경 불가능
    • alloc/initWithObjectsAndKeys
    • valueForKey
    • count
  • NSMutableDictionary: 객체의 변경 가능
    • removeAllObjects
    • removeObjectForKey
    • setValue:forKey

 

NSSet과 NSMutableSet

  • NSSet은 고유한 객체
  • NSMutableSet

 

 

Text and Strings

  • NSString은 문자열과 텍스트를 저장하는데 사용되는 클래스 중 하나
  • NSCharacterSet

 

Dates and Times

  • NSDateNSDateFormatter 클래스는 날짜 및 시간 기능을 제공
  • NSDateFormatter는 도우미 클래스

 

Excepion handling

  • NSException
    • @try
    • @catch
    • @finally

 

File handling

  • NSFileManager 클래스로 파일 처리 가능
  • 파일 처리에 사용되는 방법
    • 파일 엑세스 및 조작에 사용되는 방법 목록은 다음과 같다.
    • 여기에서 원하는 작업을 수행하려면 FilePath1, FilePaht2, FilePath 문자열을 필요한 전체 파일 경로로 바꿔야 한다.
  • 파일 삭제

 

 

4️⃣ Fast Enumeration

  • Objective-C…
for (NSString *aString in array) {
}
  • for-in 문과 유사하게 생겼다.

 

Fast Enumeration Backwards

  • reverseObjectEnumberator 메소드 실행
for (NSString *aString in [array **reverseObjectEnumberator**]) {
}

 

 

5️⃣ Memory Management

  • Objc 메모리 관리 기술
    • NSAutoReleasepol → 1.0
      • 수동으로 메모리 관리 (MRR)
      • 이거에 너무 많은 시간을 걸리니까..
    • @autoreleasepool { .. } → 2.0
      • 자동으로 메모리 관리 (only in Xcode)
      • ARC (Auto Release Count)
      • Swift에서도 비슷하게 작동한다.
  • 응용 프로그램이 불필요한 객체를 해제하지 않으면 메모리 사용 공간이 늘어나고 성능이 저하된다.

 

“Manual Retain-Relase” or MRR

수동 유지 해제

 

“Automatic Reference Counting” or ARC

자동 참조 카운팅

수동으로 해주던 역할들을.. 그 코드들을 컴파일 타임에 적절한 때에 만들어준다.

Garbage Collector쓰는 자바보다 빠르다.

  • 메모리도 덜 먹고
  • 성능도 빠른

앱이 나올 수 있던 이유이다.

 

 

 

 

 

 

 


 

 

 

 

 

이 날 내용이 굉장히 어려웠다. 그래서 Recap으로 다시 복습하니까 protocol - delegate에 대한 이해가 되었다. 하나의 예시로 두가지 개념이 있어서 이해를 바로 하기 어려웠음...

 

 

그리고 카카오 데이터센터 불나서 이제야 블로그 올린다.. ㅜ

 

 

그리고 이날 HTML도 했는데 블로그에는 따로 안올리려고 한다. 개인 노션에만 정리해두었다.

그냥 Objc 내용이 그대로 가는데 더 좋을 것 같았다.

  Comments,     Trackbacks