안녕하세요. 후르륵짭짭입니다.
오랜만에 글을 작성하게 됐습니다.
최근들어 C++과 Objective-c/C++를 사용해서 간단한 것을 만드는 작업을 하게 됐는데,
옵젝씨를 자주 사용하지 않아서,,, 많이 까먹어서,, 내용을 한번 정리 해야겠단 생각에 작성하게 됐습니다.
** 상속 **
// ClassA.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ClassA : NSObject{
double length; // Private variables
double breadth;
}
@property(nonatomic, readwrite) double height; //Property
@property (nonatomic ,readonly ) BOOL isDone;
@property (nonatomic, readwrite) NSString * name;
-(id)initWithName:(NSString *)name Andheight:(double) height;
-(NSString *) print;
@end
@interface ClassB : ClassA {
NSNumber * ageValue;
}
-(id)initWithName:(NSString *)name Andheight:(double)height Andage:(NSNumber *)age;
-(NSString *) print;
@end
NS_ASSUME_NONNULL_END
Property에 대한 속성에 대한 정리된 글은 https://blog.outsider.ne.kr/604여기를 참고하면 좋다.
// ClassA.m
#import "ClassA.h"
#import "ClassB+Private.h"
@implementation ClassA
-(id)init {
self = [super init];
length = 1.0;
breadth = 1.0;
return self;
}
-(id)initWithName:(NSString *)name Andheight:(double)height{
self.name = name;
self.height = height;
return self;
}
-(NSString *) print {
NSMutableString * inital = [[NSMutableString alloc]initWithString:@""];
NSString * result = [inital stringByAppendingFormat:@" name %@ , height %.2f",self.name, self.height];
return result;
}
@end
@implementation ClassB
- (id)initWithName:(NSString *)name Andheight:(double)height Andage:(NSNumber *)age{
self = [super initWithName:name Andheight:height];
if (self != nil){
ageValue = age;
self.string = @"Hello world";
}
return self;
}
-(NSString *)print {
NSMutableString * inital = [[NSMutableString alloc] initWithString:[super print]];;
NSString * result = [inital stringByAppendingFormat:@" age %d", ageValue.intValue];
return result;
}
-(void) print2 {
NSLog(@"Hello ClassB");
}
@end
위의 내용을 보면 Class는 NSObject를 상속 받고 시작된다.
그리고 헤더파일을 보면 ClassB는 ClassA를 상속 받는다.
또한 따로 Override를 표시를 하지않고 동일한 Method가 있다면 이를 Override로 인식한다.
따라서 아래 처럼 수행을 하면 다음 처럼 결과가 나오게 된다.
ClassA * a = [[ClassA alloc] initWithName:@"Steven" Andheight:174.8];
NSString * resultA = [a print];
NSLog(@" result : %@", resultA);
NSNumber * num = [[NSNumber alloc] initWithInt:28];
ClassB * b = [[ClassB alloc]initWithName:@"Steven" Andheight:174.8 Andage:num];
NSString * resultB = [b print];
NSLog(@" result : %@", resultB);
//result
2022-09-26 19:08:39.029133+0900 C++Test[60448:4592922] result : name Steven , height 174.80
2022-09-26 19:08:39.029709+0900 C++Test[60448:4592922] result : name Steven , height 174.80 age 28
** Category 와 Extension **
//
// ClassB+ClassBCategory.h
#import "ClassA.h"
NS_ASSUME_NONNULL_BEGIN
@interface ClassB (ClassBCategory)
-(NSString *)getCopyRightString;
-(void) PrintStringValue;
-(void) print2;
@end
NS_ASSUME_NONNULL_END
Category는 Swift의 Extension과 같은 기능을 가지고 있다.
사용방법은 @interface 확장할객체이름 (카테고리명) ,,,, @end 이렇게 해주면 된다.
이때 카테고리명이 들어가는 이유는 Implementation을 구현하기 위해서이다.
// ClassB+ClassBCategory.m
#import "ClassB+ClassBCategory.h"
#import "ClassB+Private.h"
@implementation ClassB (ClassBCategory)
-(NSString *)getCopyRightString {
return [self.name stringByAppendingFormat:@"%.2f", self.height];
}
-(void) PrintStringValue {
NSLog(@"String : %@", self.string);
}
@end
위에 처럼 (ClassBCategory)라는 이름으로 확장하고 나서 Method를 구현하면 코드를 좀 더 깔끔하게 나눠서 사용 할 수 있다.
실제로 사용할 때는 사용하는 부분에서 카테고리 헤더를 가져오면 된다.
#import "ClassB+ClassBCategory.h"
NSNumber * num = [[NSNumber alloc] initWithInt:28];
ClassB * b = [[ClassB alloc]initWithName:@"Steven" Andheight:174.8 Andage:num];
NSString * copyRight = [b getCopyRightString];
NSLog(@"copyRight : %@", copyRight);
[b PrintStringValue];
[b print2];
여기서 중요한 포인트가 하나 더 있다.
Category에 print2는 구현 되어 있지 않지만 ClassB에 print2가 구현되어 있다면 print2를 사용하게 된다.
둘중에 한 부분에만 있으면 된다. 그런데 경고가 나올 것이다. 이럴 때는 Extension을 사용해주면 된다.
//
// ClassB+Private.h
#import "ClassA.h"
NS_ASSUME_NONNULL_BEGIN
@interface ClassB ()
@property (nonatomic, readwrite) NSString * string;
@end
NS_ASSUME_NONNULL_END
Extension도 Category의 일종이다.
하지만 다른 점은 Category는 Implemention이 필요할 수도 있지만
Extension은 원래 오리진 Implementation에 구현 해주면 된다.
또한 Extension은 Property를 Private하게 사용하고 싶을 때도 사용가능하다.
위에 처럼 ClassB의 익명 Category로 Extension을 만들어주고 Property를 설정해주면 Private하게 사용이 가능하다.
그래서 위에 Category 구현부에서 private으로 사용된 string을 가져다 사용하고 있다.
그리고 너무 당연한 거지만 Category에서 원래 기존의 오리진 Header를 가져오면 공용으로 사용하는
Property에도 접근이 가능하다.
또한 Category를 Implementation에 구현 해주면 같은 파일의 Implementation에서 Private하게 끌어서 사용할 수 있다.
이렇듯 Category는 정말 다양하게 사용이 가능하다.
** Protocol **
//
// PrintProtocolDelegate.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol PrintProtocolDelegate <NSObject> //NSObject를 가진 객체만 상속 가능
@required // 필수 구현 부
-(void) processCompleted;
@optional //필수적 구현 부는 아님
-(void) printProtocol;
@end
NS_ASSUME_NONNULL_END
위에 처럼 Protocol Header 파일을 만들어 준다.
그리고 사용하고자 하는 Header 파일에 Protocol을 Import 시켜준다.
//
// ClassC.h
#import <Foundation/Foundation.h>
#import "PrintProtocolDelegate.h"
NS_ASSUME_NONNULL_BEGIN
@class ClassB; // ClassA.h를 Import 하지 않고 Header 파일에 해당 객체만 사용할 때 사용한다.
@interface ClassC : NSObject<PrintProtocolDelegate>{
ClassB * classB;
}
-(instancetype)initWithClassB:(ClassB *) object;
-(void) printName;
@end
@interface PrintClass : NSObject{
id delegate;
}
-(instancetype)initWithDelegate: (id) newDelegate;
-(void) printDetail;
NS_ASSUME_NONNULL_END
위에서는 ClassC는 PrintProtocolDelegate를 준수하게 된다.
그리고 PrintClass는 Init 시점에 PrintProtocolDelegate를 준수하고 있는 객체를 받고
printDetail Method에서 PrintProtocolDelegate의 Required Method인 processCompleted를 수행하도록 해보겠다.
@implementation PrintClass
-(id) initWithDelegate:(id)newDelegate{
self = [super init];
if (self != nil) {
delegate = newDelegate;
}
return self;
}
-(void) printDetail {
NSLog(@"PrintClass - Printing Details");
[delegate processCompleted];
}
@end
이렇게 Delegate 를 Private Instance로 저장하고 Delegate로 수행하게 해줬다.
이건 Delegate 패턴인데 Protocol을 사용한 방식이라서 위와 같이 적용 해봤다.
ClassC * c = [[ClassC alloc]initWithClassB:b];
[c printName];
PrintClass * pc = [[PrintClass alloc]initWithDelegate:c];
[pc printDetail];
당연한 내용이지만 Protocol Header 파일을 Import 해줘야한다.
** Struct **
//
// TempStruct.h
#import <Foundation/Foundation.h>
struct Books{
NSString * title;
NSString * author;
NSString * subject;
};
옵젝씨에서 Struct는 C 언어의 Struct를 그대로 사용한다.
그래서 이렇게 Header에 Struct를 지정해주고 아래 처럼 사용하면 됩니다.
-(void) printStruct:(struct Books) book{
NSLog(@"subject %@ author %@", book.subject, book.author); //Value Struct 사용할 때
}
-(void) printPointerStruct:(struct Books *) book{ //Pointer 방식
NSLog(@"subject %@ author %@", book->subject, book->author); //참조형 Struct 사용할 때
}
-(struct Books *) changeStruct:(struct Books *) book WithAuthor : (NSString *)value{
book->author = value;
return book;
}
//참조형으로 Struct 내부 값을 변경 할 때
아래 처럼 사용 한다.
그런데 TypeDef 방식이 아니기 때문에 앞에 struct라고 명시를 해줘야한다.
struct Books item1;
item1.subject = @"Steven";
[pc printStruct:item1];
struct Books * item2 = [pc changeStruct:&item1 WithAuthor:@"HelloWorld"];
[pc printPointerStruct:item2];
[pc printPointerStruct:&item1];
만약에 TypeDef 방식을 사용한다면 아래 처럼 사용 할 수 있다.
//정의
typedef struct IMG {
NSString *title;
int img_id;
} Pictor;
//사용
Pictor img;
img.img_id = 100;
img.title = @"Hello";
이렇게 기본적은 Objective - c를 알아봤다.
Swift가 나오기 전에는 Objective-C가 IOS의 근본적인 개발 언어였지만 이제는 Swift로 변경 되었다.
하지만 Objective-C가 완전 필요없어진 언어라고 생각 했지만 Multi Module을 사용할 때는 지속적으로 필요하다는 것을 느끼게 된다.
그래서 앞으로 C++에 대해서도 공부하고 Objective-C에 대해서도 Swift Combine도 해야하고
할 것이 너무 많다....
** 참고 사이트 **
Objective-C 정리 :
https://aroundck.tistory.com/3433
Access private property from category :
Private Property 사용하는 방법 :
https://inchoo.net/dev-talk/ios-development/how-to-add-a-property-via-class-category/
Property 정리 글 :
https://blog.outsider.ne.kr/604
@Class ??? :
https://stackoverflow.com/questions/3904663/what-does-class-do-in-objective-c
'Xcode > Swift - PlayGround' 카테고리의 다른 글
PlayGround) Framework를 통해 모듈화 작업하기 (5) | 2022.11.27 |
---|---|
PlayGround) Swift Combine 적응기 #1 (Custom Publisher) (0) | 2022.11.05 |
PlayGround) Async - Await 경험 정리#1 (0) | 2022.08.13 |
PlayGround) RxTest에서 Timer들어간 Observable 테스트 (0) | 2022.04.24 |
PlayGround) Subscript란? (0) | 2022.03.15 |
댓글