[iOS] 익스텐션 (Extension)

아이폰 2015. 8. 18. 21:12

1. iOS 익스텐션 개요

  • 익스텐션은 애플리케이션의 특정기능을 다른 애플리케이션에서 사용될 수 있게 하기 위해 설계된 iOS8의 한 부분.
  • 익스텐션의 목적은 특정 기능을 다른 애플리케이션에서 접근할 수 있도록 하는 것.


2. 익스텐션 타입

  • iOS는 익스텐션 포인트(extension point)로 지시되는 익스텐션 타입을 제공.
  • 익스텐션 포인트는 익스텐션이 구현될 수 있도록 공개한 iOS 운영체제의 영역.


1. 투데이 익스텐션(Today Extension)

  • iOS 알림센터의 Today 화면에서 사용할 수 있는 익스텐션.
  • 디폴트로 캘린더 정보와 우세한 주식가격 정보를 표시.
  • 사용자에게 정보를 표시하는 위젯형태를 취함.



2. 쉐어 익스텐션(Share Extension)

  • 소셜 네트워크 사이트 또는 컨텐츠 공유 서비스의 호스트앱에서 이미지, 비디오, 텍스트, 웹사이트 등의 컨텐츠를 공유하기 위한 빠른 접근 매커니즘 제공.
  • 쉐어 익스텐션은 사용자가 호스트앱에서 공유버튼을 눌렀을 때 표시되는 액티비티 뷰 컨트롤러 패널에 표시됨.
  • 컨텐츠 포스팅을 위한 인터페이스 구현을 위해 SLComposeServiceViewController클래스 이용.
  • SLComposeServiceViewController는 UI 커스터마이징이 가능하며, 포스팅하기 전에 수정할수 있는 기능을 제공.



3. 액션 익스텐션(Action Extension)

  • 호스트 앱 내의 컨텐츠가 다른 식으로 변형되거나 보여지도록 만듬.
  • 쉐어 익스텐션처럼 공유버튼을 통해 액티비티 뷰 컨트롤러로 접근.
  • 호스트앱의 콘텐츠 타입을 지원하는 액션 익스텐션만 옵션으로 표시됨.



4. 포토 에디팅(Photo Editing Extension)

  • 애플리케이션의 사진 편집 기능을 내장된 iOS 사진 앱 내에서 접근할 수 있도록 해줌.
  • 사진앱에서 이미지 선택후 편집 옵션을 선택한 다음 ... 버튼을 탭할 때 표시됨.



5. 도큐먼트 익스텐션(Document Extension)

  • 다른 애플리케이션이 디바이스에서 실행되는 동안 (익스텐션을) 포함하는 앱이 문서 저장소처럼 동작하도록 함.
  • Document Picker ViewController 익스텐션과 File Provider 익스텐션으로 구성됨.
  • Document Picker ViewController 익스텐션은 문서들을 검색하거나 선택할 수 있는 사용자 인터페이스를 제공함.
  • File Provider 익스텐션은 호스트앱이 앱의 샌드박스 외부에 있는 문서에 접근할 수 있도록 해줌.
  • 포함하는 앱에서 저장된 문서를 이동하거나 여는 작업을 지원해야 한다면 파일 프로파이더 익스텐션이 필요함.



6. 커스텀 키보드 익스텐션(Custom Keyboard Extension)

  • iOS 디바이스에 커스텀 키보드를 생성하고 설치할 수 있도록 함.
  • 디바이스의 모든 애플리케이션에서 사용될수 있음.
  • 설정앱의 키보드 설정에서 선택할수 있음.




[swift] Swift 기초 - 4

Swift 2014. 10. 29. 13:32



4번의 Swift 스터디 자료중 마지막 자료입니다. 





지난 시간에 학습한 내용을 간단히 정리해볼까요?

구조체는 member-wise 생성자를 디폴트로 가지며, pass by value 즉 복사되어 함수에 전달됩니다. 

클래스는 designated / convenience 생상자를 갖으며, pass by reference 즉 참조로 함수에 전달됩니다.

구조체,클래스의 속성으로는 저장속성, 계산속성이 있으며, 속성 옵저버에 대해 간략히 배웠습니다.

서브스크립트를 구현하면 클래스의 데이터를 인데스 문법을 사용하여 접근할 수 있습니다. 

데이터 초기화 역할을 하는 생성자, 그리고 소멸자는 사용한 자원을 해제하는 역할을 담당합니다. 




이번 시간에는...

사용한 객체의 참조 횟수를 추적해서, 필요없을 때 자동으로 해제해주는 ARC, 

상속에 의존하지 않고 클래스를 확장할 수 있는 Extension,

Java 언어에서 인터페이스 역할을 하는 Protocol, 

타입을 인자로 하여 범용타입의 자료구조를 정의할 수 있게 해주는 Generic에 대해 알아보겠습니다.






iOS5.0 부터 Objective-C에서 메모리 관리를 획기적으로 쉽게 만든 ARC는 Swift에서 기본으로 지원합니다.

Automatic Reference Counting 이라는 이름 그대로, 객체마다 참조되는 횟수를 추적하여 

참조횟수가 0이 될때, 자동으로 메모리에서 해제시켜주는 기법입니다. 

ARC의 동작과정을 알아보기 위해,  생성,해제 시점에 출력하는 Person 클래스를 정의하였습니다.




위 할당문 사이사이에 코드 출력문을 삽입하고, 어떤 결과가 나오는지 지켜볼까요?




Person 객체를 참조하는 변수 ref1, ref2, ref3 모두 nil이 되는 순간, 

즉 레퍼런스 카운트가 0이 되는 순간 자동으로 객체가 해제되는 것을 볼 수 있습니다.





사람(Person)이 입주할 수 있는 아파트(Apartment) 클래스를 정의하였습니다. 




위 코드를 실행하면 다음 결과를 출력합니다.



Person 객체의 초기화는 이루어졌지만, 해제가 되지 않았습니다. 무엇 때문일까요? 

네. Apartment 객체의 tenant 속성이 Person을 참조하고 있고,

Person의 home 속성이 Apartment을 참조하고 있기 때문입니다.

이렇게 서로 참조하는 상황을 순환참조(reference cycle)라고 합니다. 

순환참조가 이루어지면 메모리 해제되지 않고 남아있는  메모리 누수가 발생합니다.

Swift에서는 순환참조를 방지하기 위해 2가지 키워드를 제공합니다. 




weak 키워드를 사용하면, 참조횟수를  1증가시키지 않고, 약한참조로 동작합니다. 

참조하는 객체가 메모리에서 해제되면 weak 참조변수는 자동으로 nil로 초기화되어 안정성을 보장합니다.

이런 특성 때문에 optional 타입변수만 weak 참조변수가 될수 있습니다. 



weak 참조는 참조 카운트를 증가시키지 않기 때문에, 

renters["Elsvette"] = nil 실행과 동시에 Person 객체가 해제되고, 

apts[507] = nil 의 의해 Apartment 객체가 해제됩니다.





순환참조를 방지하는 또다른 키워드는  unowned Reference 입니다. 

신용카드(CreditCard)를 소유하는 고객(Customer) 클래스를 정의하였습니다.





신용카드(CreditCard) 클래스는  카드를 소유하는 고객(Customer)을 unowned 로 참조합니다. 

unowned 참조는 말그대로 소유권을 갖지않는 참조로 역시 참조 카운트를 증가시키지 않습니다. 

unowned 참조변수는 nil로 초기화되지 않으며, optional 타입에 사용할 수 없습니다. 

unowned 참조는 보통 생명주기가 타 클래스에 의존적인 클래스에 주로 사용됩니다.

신용카드는 소유주인 고객이 존재하는 동안만 의미를 갖는다는 것을 생각해보세요.




코드 실행결과를 볼까요?



고객(Customer) 객체가 해제되고 신용카드 객체도 해제되어 순환참조 문제가 발생하지 않습니다.


정리하면, 모델링 하는 대상 클래스들의 의존성 여부와 optional  가능유무에 따라 

weak와 unowned 를 구분하여 사용하면 되겠습니다! 





자바에서는 인터페이스, Objective-C, Swift에서는 프토토콜 이라고 합니다. 

프로토콜의 메소드는 디폴트로 required로 동작합니다. 

Thing를 상속하는 Boards 클래스는 Pullable 프로토콜 메소드 pull()을 

구현하지 않았기 때문에 컴파일 에러가 발생합니다. 





이렇게 pull() 메소드를 구현하면 컴파일 에러가 발생하지 않습니다.




인스턴스가 프로토콜을 따르는지 유무를 알기 위해, is, as?, as! 연산자를 사용할 수 있습니다.

is는 특정 프로토콜을 따르는 경우 true,  그렇지 않으면 false를 반환합니다.

as?는 특정 프로토콜을 따르면 해당 타입으로 변환해주고, 그렇지 않으면 nil을 반환합니다.

as!는 특정 프로토콜로 강제 형변환을 시도하며, 실패할 경우 런타임 에러가 발생합니다. 






Extension은 Objective-C에서 카테고리와 유사합니다.  

기존에 존재하는 클래스를 변경하지 않고, (기능을) 확장할 수 있습니다.

코드의 재사용성을 획기적으로 높일 수 있는 Swift 언어의 장점 중 하나입니다.

Double 클래스를 확장하는 Extension을  만들고, 거리단위를 환산하는 메소드를 정의했습니다.





Extension 에 정의한 메소드를 활용하면, 짧고 명료한 방법으로

다양한 단위의 Double 형 부동소수값을  미터 단위로 변환할 수 있습니다.





역시 정수형 횟수만큼 특정 task 함수를 반복하는 코드를 쉽게 정의할수 있습니다.





정수형에 제곱을 구하는 Extension 메소드입니다.






Extension은 서브스크립트와도 조합하여 사용할 수 있습니다. 

index에 해당하는 십진수의 자리 수를 구하는  subscript를 정의하였습니다.





문자의 자음, 모음 유무를 출력하는 Extension 메소드를 정의하였습니다.





다음과 같은 결과를 출력합니다.






제네릭은 컬렉션, 구조체, 클래스에 타입을 매개변수화하여 전달할 수 있는 기능입니다. 

만일 제네릭이 없다면, 스택 데이터 구조를 정의할 때, 정수형, 문자형, 부동소수형 등 모든 타입에 대해

스택 데이터 구조를 각각 정의해야 할 것입니다.

제네릭은 타입에 대한 유연성과 안정성을 제공해주는 유용한 기능입니다.





앞서 정의한 정수형 스택에서, 타입T를 매개변수화하여 제네릭으로 정의하였습니다.





동일한 코드로, 정수형, 문자열 등 다양한 타입의 스택을 사용할 수 있습니다.



4번의 스터디를 통해 간략하게 나마 Swift 언어에 대해 훑어보았습니다.  

Swift 언어는 지금도 시시각각으로 변화하고 있으며 발전하고 있습니다. 

혹자는 어차피 바뀔건데 왜 벌써 공부해 ?라고 할수도 있지만, 무엇이든지 변화되는 과정을

지켜보는 것은 즐겁습니다. 그 변화 과정속에서 혹시 남들이 보지못하는 혜안을 가지게 될지 모르잖아요?






'Swift' 카테고리의 다른 글

[swift2] New in Swift2.0  (0) 2015.06.19
[swift] Apple Swift Blog  (0) 2015.06.10
[swift] Swift 기초 - 4  (0) 2014.10.29
[swift] Swift 기초 - 3  (1) 2014.10.29
[swift] Swift 기초 - 2  (0) 2014.10.29
[swift] Swift 기초 - 1  (1) 2014.10.29

Protocol 과 Category

Objective-C 2011. 3. 22. 23:53



이번 포스팅에서는 Protocol과 Category에 대해서 알아보자.  Protocol은 자바의 인터페이스에 유사한 개념이다. 
Protocol은 공식 프로토콜과 비공식 프로토콜이 있으며, 그 중 공식 프로토콜이 자바의 인터페이스에 해당한다.
Category는 상속의 대안으로 클래스를 변경하지 않고도, 새로운 기능(메서드)을 추가할 수 있는 메커니즘이다. 
프로토콜과 카테고리에 대해서 좀 더 자세히 알아보자. 


1. Protocol 이란? 
자바언어의  Interface에 해당하는 개념으로 @protocol 지시어를 사용하여 정의한다. @protocol 을 구현하는 모듈은
프로토콜이 규약하는 모든 메소드를 반드시 구현해야하는 프로토콜을 공식 프로토콜(Formal Protocol)이라고 하며, 
구현해도 되고 구현하지 않아도 되는 프로토콜을 비공식 프로토콜 (Informal Protocol)이라고 한다.  공식 프로토콜은 
@required 지시어를,  비공식 프로토콜은 @optional 지시어를 사용한다.  
(비공식 프로토콜은 런타임시에 비공식 프로토콜을 구현하는 메소드가 존재하는 경우에만 호출된다.)
공식프로토콜의 모든 메소드들이 구현되지 않으면 컴파일 시에 에러가 발생할 것이다.  2개 이상의 프로토콜을 구현할
경우 ,(콤마)를 구분자로  구현할 프로토콜을 <> 안에 나열한다.

 Objective-C 에서는 Java 와는 다르게 컴파일 타임에 프로토콜 할당에 대해 타입검사를 수행하지 않는다.
 런타임시 메시지가 호출될 때, 해당하는 메소드가 구현되지 않았다면 selector  exception 예외가 발생한다. 

@interface VenusAttacks : Game
...
@end

@class Thing;

@protocol Living
- (float)age;
- (float)health;
(NSDictionary*)healthInfo;
@end

@protocol Communicating
- (NSArray*)recipientsInRange;
- (void)sendMessage:(NSString*)message to:(id)recipient;
@end

@interface Thing : NSObject 
...
@end

@interface Character : Thing 
...
@end




1.1 비공식 프로토콜(Informal Protocol)
비공식 프로토콜은 모듈클래스가 반드시 구현하지 않아도 되는 프로토콜이다.
비공식 프로토콜은 (반드시 구현하지 않아도 되기 때문에) 유연하고 동적이지만,  추가적인 문서화와 프로그래머들 간에
(통일된 사용을 위해) 협조가 요구된다.  비공식 프로토콜에서는  메서드가 구현되었는지 검사하는 코드가 들어가며  
그 형태는 아래 코드와 같다. 자바에서는 윈도우 종료시 이벤트를 인터셉트 하기위해서,  해당하는 이벤트에 대한 리스너를
등록하고, 이벤트가 발생하면 리스너를 통해 통보받는다.  Objetive-C 에서는  delegate 가 윈도우 종료시 Notification 을
통보받는 메소드를 비공식 프로토콜로 선언하고 있다. 
 
BOOL shouldClose = YES;
if ([delegate respondsToSelector:@selector(windowShouldClose:)])
   shouldClose = [delegate windowShouldClose:self];
if (!shouldClose)
return;





1.2 공식프로토콜과 비공식 프로토콜의 조합(Combining Formal and Informal Protocols)

프로토콜은 비공식 프로토콜과 공식 프로토콜이 혼합되어 사용되어지는 경우가 많다.
프로토콜에 선언한 메소드는 디폴트로 공식 프로토콜이며, @required 이라는 지시어를 명시적으로 사용할 수 있다. 
비공식 프로토콜은 @optional  지시어를 사용한다.

다음은 TableDataSource 프로토콜이며, 테이블 행의 개수를 반환하는 메소드와 해당하는 열과 행의 셀객체를 반환하는 
메소드는  @required 로 , 테이블 특정 행과 열의 셀객체를 변경하는 메소드는 @optional 로 선언되어 있어서, 비공식 프로토콜이
구현되지 않았을 경우,  read-only 로 동작하게 된다. (구현 유무에 따라 동작용도를 변경할 수 있다. )

@protocol TableDataSource
@required
- (int)numberOfRowsInTable:(Table*)table;
        - (id)table:(Table*)table objectForColumn:(int)col row:(int)ro
@optional
- (void)table:(Table*) setObject:(id)object forColumn:(int)col row:(int)row;
@end

 





 
2. 카테고리(Categories)
클래스 정의 일부에 이름을 붙힌 조각이라 할 수 있다..  자바에서는 단일블럭에서 하나의 클래스 정의가 모두 이루어지지만, 
Objective-C에서는 한 클래스 정의를 카테고리를 사용하여 여러 파일에 분리하여 정의할 수 있다.
카테고리를 사용함으로써, 복잡한 클래스를 관련이 되어 있는 인스턴스 변수와 메소드들의 그룹으로 분리하여
복잡성을 줄이고,  프로그래머들이 독립적으로 개발할 수 있도록 해준다. 
이외에도 카테고리는 클래스 정의(@interface) 와 독립적으로 클래스 메소드를 추가할 수 있게하며,
클래스 구현의 일부를 은닉할 수 있는 메커니즘을 제공한다. 클래스와 카테고리는 별도의 헤더파일과 모듈파일에
저장될 수 있으며, 해당 클래스를 사용하는 클라이언트 클래스는 관련있는 헤더파일만을  import 하여 사용한다. 

// 카테고리를 사용하지 않고 단일 클래스에 정의한 경우

@interface RecipeBoxController : NSObject {
       NSMutableArray*recipes;
NSMutableDictionary*  recipeIndex;   
}
- (id)init;
- (Document*)newRecipe;
- (Document*)newShoppingList;
- (Document*)newShoppingListFromRecipes:(NSIndexSet*)recipeIndexes;
@end
@implementation RecipeBoxController
...
@end

다음 카테고리는 리스트 설정하고 획득하는 역할만을 수행한다.
// Category 를 사용하여 클래스를 분리 
@interface RecipeBoxController (ShoppingLists)
- (Document*)newShoppingList;
- (Document*)newShoppingListFromRecipes:(NSIndexSet*)recipeIndexes;
@end

@implementation RecipeBoxController (ShoppingLists)
...
@end




2.1 카테고리 사용하여 조직하기(Using Categories for Organization)
거대한 클래스를 다룰수 있는 기능적인 단위로 나누는 것이 카테고리의 주요한 용도이다.  이 개념은 한 클래스를 
모듈화 할 수 있게하며, 각 모듈간에 캡슐화와 의존성을 줄여준다. 빌터패턴이 카테고리를 사용하여 얻을 수 있는 응용의 한
예이다. 자바에서 빌드에서 복잡한 객체 생성의 과정이 별도 클래스로 분리되는데, 
Objective-C에서는 별도의 객체생성과정이 메인 클래스 정으로부터 카테고리를 사용하여 분리될 수 있다. 


 

  2.2 메서드 은닉 (Hiding Methods)
카테고리의 또 다른 사용용도는 클래스의 일부를 은닉하는 것이다. 자바에서는 private, protect 키워드를 사용하여
내부 데이터를 은닉한다.  아래는 카테고리를 사용하여 메소드 일부를 은닉하는 예이다.  Private 이름으로 선언된 카테고리는
컴파일러에게 무시되며, 해당 클래스를 사용하는 클라이언트 클래스는 카테고리의 메소드들을 볼 수 없다. 
두개의 클래스 선언을 별도의 헤더 파일로 분리할 수도 있다.  이때, 클라이언트 클래스는 ToasterController.h 헤더파일만을  
import 하여 사용하면 된다. 카테고리의 헤더파일은 관례적으로  (클래스이름+카테고리이름.h) 이라는 형태를 갖는다.
아래에서는 ToasterController+Private.h 라는 이름을 갖을 것이다.
이런한 개념을 확장하여 Objectiv-C 에서는 extension 이라는 개념을 제공한다.

@interface ToasterController : NSObject {
   @private
float darkness;
}
- (void)setDarkness:(float)level;
- (void)startToasting
- (void)stopToasting
@end

@interface ToasterController (Private)
- (float)darkness
- (CarrierState)carrierPosition;
- (NSTimeInterval)cycleTime;
- (void)setCycleTime:(NSTimeInterval)cycleTime;
- (void)finishedToasting:(NSTimer*)timer;

 
2.2 외부 클래스 확장 (Augmenting Foreign Class)
카테고리를 사용하여 원 클래스에 어떠한 변경을 하지 않고도, 새로운 메소드를 정의할 수 있다. 
아래 코드는 Foundation Framework 의 NSArray 클래스에  카테고리를 사용하여, 배열의 첫번째 요소를 얻는
(id)firstObject 메소드를 추가하고 있다.  

* 만일 카테고리와 클래스가 동일한 메소드를 구현한다면, 카테고리 메소드가 클래스의 메소드를 대체하게 되어, 본 클래스의
메소드를 호출하지 못하게 된다.  동일한 메소드를 구현하는 카테고리가 2개 이상 있게되면 런타임에 무엇이 호출될지는 예측할 수 없다.
@interface NSArray (MyCollectionAdditions)
- (id)firstObject;
@end
@implementation NSArray (MyCollectionAdditions)
- (id)firstObject {
   return ([self objectAtIndex:0]);
}
@end




2.3 Extesions
Extension 은 익명 카테고리이다. 다만 Extension은 클래스의 @implementaion 구현부아 아닌  클래스의 @interface 정의부를
분리하기 위해 사용한다. extension 의 @interface 지시어는 비어있는 카테고리 이름을 사용한다.  extensions 내에 선언된 메소드들은 동일하게 @implentation  에서 모두 구현되어져야 한다. 카테고리와 extension은 컴파일 타임에만 프로그래머와 컴파일러로부터 메소드 선언을 은닉할 수 있으며, 런타임시에는 introspection(리플렉션)을 사용하면 노출될 수 있다. 

@interface CaseDocument : NSObject {
@private
   CaseNumber caseNumber;
}
- (CaseNumber)caseNumber;
@end
@interface CaseDocument()
- (void)setCaseNumber:(CaseNumber)number;
@end
@implementation CaseDocument
- (CaseNumber)caseNumber { return (caseNumber); }
- (void)setCaseNumber:(CaseNumber)number { caseNumber = number; }



 
 

'Objective-C' 카테고리의 다른 글

Objective-C 문자열 조작 메서드  (0) 2011.04.29
다중인자를 갖는 PerformSelector 메시지  (0) 2011.03.23
Protocol 과 Category  (0) 2011.03.22
Objective-C 훑어보기  (2) 2011.03.20
[Cocoa] Cocoa Design Pattern  (0) 2011.03.20
HMAC-SHA1 구현  (0) 2011.03.03