본문 바로가기
프로그래밍 놀이터/iOS

[iOS Study] Objective-C

by 돼지왕 왕돼지 2016. 2. 13.
반응형

 [iOS Study] Objective-C


출처 : 아론 힐리가스의 iOS 프로그래밍

#import, %@, +, ., .pch, @, @end, @implementation, @import Foundation;, @interface, alloc, arc4random, Argument, c 구조, c 구조체, Caching, Cocoa Touch, convenience method, convention, C언어, decription, H, ID, immutable array, import, init, instancetype, IOS, ISA, literal, M, nested message send, nil, nil 포인터, NSData, nsnull, NSNumber, nsobject, NSString, NSValue, objectAtIndex, Objective-C, overloading, PCH, precompiled header, precompiled header file, preprocessing, Protected, rand, receiver, SEED, selector, Self, Study, tutorial, zero pointer, [iOS Study] Objective-C, _, 가독성, 개념, 객체, 객체 참조, 객체 포인터, 관리, 구현 블록, 구현 파일, 규칙, 기본형, 네임스페이스, 단일 상속, 단축형, 대괄호, 리시버, 마스터 헤더, 매개변수, 메모리, 메모리 주소, 메소드, 메시지, 밑줄, 반환 타입, 반환값, 배열, 변수, 변수명, 보장, 블록, 상속, 상위 클래스, 셀렉터, 암시적 지역 변수, 예외, 오류, 유효, 이름, 인스턴스 메소드, 인스턴스 변수, 인스턴스 변수명, 인자, 임포트, 전처리, 점, 점 표기법, 접근자 메소드, 접두사, 제로 포인터, 주소, 중 괄호, 중첩 메세지 보내기, 지정 초기화 메소드, 차이, 초기화, 초기화 메소드, 초기화 메소드 체인, 캐시, 컨비니언스 메소드, 컴파일, 코드, 코코아 터치, 코코아 터치 프레임워크, 콘솔, 콜론, 크래시, 클래스, 클래스 메소드, 클래스 이름, 클래스 헤더, 파싱, 포맷팅 문자, 포인터, 포함, 표준 헤더, 프레임워크, 하위 클래스, 해당 파일, 헤더, 헤더 파일, 확인, 확장, 회사 이름, 힙


-
iOS 앱은 코코아 터치(Cocoa Touch) 프레임워크를 사용하여 Objective-C 언어로 작성된다.
Objective-C 는 C 언어의 확장이며 코코아 터치 프레임워크는 Objective-C 클래스의 모음이다.


-
일반적으로 Objective-C 에서는 인스턴스 변수명을 밑줄(_) 로 시작한다.


-
객체를 만들기 위해서는 클래스에 alloc 메세지를 보낸다.
이에 클래스는 메모리(힙)에 객체를 만들고 변수에 저장된 그 객체의 주소를 준다.


-
Party* partyInstance = [Party alloc];
[partyInstance init];

새로 할당한 인스턴스에 보내는 첫 메시지는 항상 초기화 메시지다.
인스턴스를 만들기 위해 클래스에 alloc 메시지를 보내더라도 그 인스턴스는 초기화되기 전까지 유효하지 않다.


-
[[Party alloc] init];
위와 같은 문법을 중첩 메세지 보내기(nested message send) 라고 한다.


-
메시지는 항상 대괄호 안에 포함된다.
대괄호 안에 메시지는 세 부분으로 나뉜다.

리시버(receiver) : 메시지를 실행할 객체의 포인터
셀렉터(selector) : 실행한 메소드의 이름 ( 모든 라벨을 합친 것 )
인자(arguments) : 실행할 메소드에 매개변수로 전달되는 값들

라벨과 인자가 쌍을 이루는 것은 Objective-C 의 중요한 특징이다.


-
메소드는 시랭 가능한 코드의 모음이고, 메시지는 클래스나 객체에게 메소드를 실행하도록 요청하는 행위이다.


-
객체를 제거하려면 객체를 가리키는 변수를 nil 로 설정한다.
nil 변수는 제로 포인터(zero pointer) 이다.


-
포인터가 nil 인지 확인하기 위한 단축형은..
if ( !venue )


-
만약 nil 인 변수에 메시지를 보내면 아무 일도 벌어지지 않는다.


-
만약 프로그램이 무언가 해야 하는 시점에 아무런 동작도 하지 않는다면 예상하지 못한 nil 포인터가 종종 원인일 수 있다.


-
모든 클래스는 NSObject 에 정의된 메소드와 인스턴스 변수를 상속한다.


-
%@ 는 어떤 객체 포인터와도 일치하는 포맷팅 문자이다.
그 인자는 description 메세지에 보내진다.
description 메소드는 토큰을 대신하는 NSString 인스턴스를 반환한다.

%@ 토큰에 상응하는 인자는 메시지로 보내지기 때문에 반드시 객체여야 한다.






-
.h 헤더 파일은 새 클래스 이름, 상위 클래스, 인스턴스 변수, 구현할 메소드 등을 선언한다.
.m 파일은 구현 파일로 구현하는 메소드의 실제 코드를 갖는다.

헤더 파일을 클래스 인스턴스의 사용 설명서로 생각할 수 있고,
구현 파일은 실제 동작하는 법을 정의한 세부 기술로 생각할 수 있다.


-
클래스를 선언하려면 @interface 키워드를 클래스 이름 앞에 사용한다.
콜론(:) 다음에 상위 클래스 이름이 온다.
Objective-C 는 단일 상속만을 허용한다.
@end 지시자는 클래스 선언의 끝을 가리킨다.


-
클래스의 인스턴스 변수는 클래스 선언 다음의 중괄호 {} 사이에 선언한다.
그것들은 기본으로 protected 의 성격을 갖는다.


-
Objective-C 에서 세터 메소드의 이름은 그저 인스턴스 변수이름 그대로이다.


-
구현 파일의 맨 위에는 항상 그 클래스의 헤더 파일을 임포트(import) 한다.
임포트는 해당 파일을 한 번만 포함하는 것을 보장한다.


-
import 문 다음은 @implementation 키워드와 구현할 클래스 이름으로 시작하는 구현 블록이다.
구현 파일에서 모든 메소드 정의는 이 구현 블록 안에 있다.
메소드들의 정의는 @end 키워드가 나타나 블록이 닫힐 떄까지 계속된다.


-
리시버 뒤에 .(점)이 따라오고 그 뒤에 밑줄 없는 인스턴스 변수명이 온다.
이는 접근자 메소드를 호출하는 것과 동일하다.
최근 Objective-C 프로그래머들은 접근자를 호출하기 위해 점 표기법을 사용하는 경향이 있다.
코드를 읽기 쉽게 만들어주고, 중첩된 메시지 호출을 해야 할 때 읽기 쉬워 좋다.
애플의 코드와 일관성도 유지된다.


-
NSObject 인스턴스에 description 메시지를 보내면
해당 객체 클래스 이름과 메모리 주소를 반환한다.


-
클래스 내부에 있는 인스턴스 변수라도 접근자 메소드를 사용하는 것이 좋다.


-
각 초기화 메소드는 init 이라는 단어로 시작한다.
초기화 메소드 명명법이 다른 인스턴스 메소드와 어떠한 차이를 만들어내는 것은 아니다.
단지 이름을 나타내는 방식이다.


-
지정 초기화 메소드는 객체의 모든 인스턴스 변수를 유효하게 만든다.
유효하다는 것은 객체 초기화 후에 메시지를 보낼 때 결과를 예측할 수 있고 아무런 문제가 없음을 뜻한다.


-
instancetype ( 이전에는 id ) 키워드는 반환 타입으로만 사용할 수 있고, 그 반환 타입은 메시지의 리시버와 일치한다.
init 메소드는 항상 instancetype 을 반환하도록 선언된다.
왜 반환 타입이 포인터가 아닐까?
그 이유는 해당 클래스가 하위 클래스를 가진 경우 문제가 될 수 있기 때문이다.


-
Objective-C 에서는 같은 셀렉터와 다른 반환 타입(또는 인자)을 가진 형태의 메소드를 여러 개 가질 수 없다.
( overloading 을 지원하지 않는다. )


-
Objective-C 에 instancetype 키워드가 생기기 전에는 초기화 메수드가 id 를 반환했다.
id 는 임의의 객체에 대한 포인터로 정의되어 있다.
id 는 객체의 타입이 불확실한 경우 id 타입의 변수나 메소드 인자를 선언할 수 있다.

id 는 임의의 객체에 대한 포인터로 정의돼 있기 때문에 id 타입의 변수나 메소드 인자를 선언할 때 *를 포함하면 안 된다.


-
메소드 구현부의 지정 초기화 메소드에서 항상 맨 먼저 하는 것은 super 를 사용해 상위 클래스의 지정 초기화 메소드를 호출하는 것이다.
마지막으로 하는 것은 self 를 사용해 초기화에 성공한 객체 포인터를 반환하는 것이다.


-
self 는 암시적 지역 변수이다.
init 메소드의 마지막 줄에서 항상 새로 초기화한 객체를 반환한다.

일반적으로 self 는 객체가 그 자신에게 메시지를 보내기 위해 사용된다.


-
상위 클래스의 초기화 메소드를 호출한 다음에는 그 반환값을 확인해야 한다
만약 초기화 메세지가 실패하면 nil 을 반환할 것이다.


-
인스턴스 변수에 직접 접근하지 말고 접근자 메소드를 이용하라고 언급했었다.
하지만 초기화 메소드를 작성할 때는 그 규칙을 깨는 것이 좋다.


-
초기화 메소드 체인을 사용하면 오류의 가능성이 줄고 코드를 관리하기 쉽다.
클래스를 만든 프로그래머들은 어느 초기화 메소드가 지정 초기화 메소드인지 명확히 한다.
지정 초기화 메소드에서 초기화 코드를 하 번만 작성하면 다른 초기화 메소드에서는 단순히 기본값으로 지정 초기화 메소드를 호출하기만 하면 된다.


-
초기화 메소드의 규칙은 다음과 같다.

* 클래스는 상위 클래스로부터 모든 초기화 메소드를 상속받고 원하는 만큼 추가할 수 있다.
* 각 클래스는 하나의 초기화 메소드를 지정 초기화 메소드로 선택한다.
* 지정 초기화 메소드는 제일 먼저 상위 클래스의 지정 초기화 메소드를 호출한다.
* 다른 초기화 메소드는 클래스의 지정 초기화 메소드를 호출한다.
* 클래스가 상위 클래스와 다른 지정 초기화 메소드를 선언하면 새 지정 초기화 메소드를 호출하도록 상위 클래스의 지정 초기화 메소드를 반드시 재정의해야 한다.


-
클래스 메소드는 인스턴스에서 동작하지 않고, 어떤 인스턴스 변수에도 접근하지 않는다.
클래스 메소드는 + 기호를 사용한다.


-
인스턴스 변수가 맨 처음 오고 다음에 클래스 메소드가, 그리고 초기화 메소드와 다른 인스턴스 메소드가 그 뒤를 따라온다.
가독성이 좋은 convention 이다.


-
array 를 literal 로 정의할 때는 대괄호 앞에 @ 를 붙인다.
이는 Immutable array 를 생성한다.


-
arc4random() 함수는 자체적으로 seed 를 하며, return 값의 범위가 rand() 보다 넓어서 사용하기 훨씬 좋은 녀석이다.






-
Objective-C 배열은 여러 타입의 객체를 포함할 수 있다.
Objective-C 객체에 대한 참조만을 저장할 수 있다.
기본형과 C 구조체는 Objective-C 배열에 추가할 수 없다.
만약 기본형이라 C 구조체를 저장하려면 NSNumber, NSValue, NSData 와 같은 Objective-C 객체로 해당 데이터를 감사 사용할 수 있다.

또한 배열에 nil 을 추가할 수 없다.
만약 배열에 nil 을 추가하고자 한다면 반드시 NSNull 을 사용해야 한다. ( [NSNull null] 로 생성 )


-
자신의 타입의 객체를 반환하는 클래스 메소드를 컨비니언스 메소드(convenience method) 라고 한다.
컨비니언스 메소드에서 self 를 이용해서 객체를 생성해야 한다.
그래야 하위 클래스에서 사용할 때 문제가 없다.


-
[index] 를 사용한 배열 접근은
objectAtIndex: 메시지를 보낸 것과 동일하다.


-
모든 객체는 isa 라는 인스턴스 변수를 가진다.
객체가 만들어지면 객체를 만든 클래스는 반환된 객체의 isa 변수를 다시 자신을 가리키도록 설정한다.


-
어떤 이유로든 응답하지 않는 객체에 메세지를 보내면 프로그램은 예외가 발생할 것이다.


-
프로그램이 중단되거나 크래시가 발생하면 항상 콘솔을 확인하자.


-
Objective-C 에는 네임스페이스라는 개념이 없다.
대신에 클래스 이름을 구분하기 위해 두서너 자의 접두사를 사용한다.
자신의 이름, 회사 이름, 포함될 라이브러리 등과 관련된 것을 접두사로 사용한다.


-
세 글자의 접두사를 사용하는 것이 좋다.
애플에서 많은 두 글자 접두사를 프레임워크 클래스용으로 사용하고 있기 떄문이다.


-
예전에는 프레임워크를 위한 마스터 헤더 파일을 #import 했다.
이 마스터 헤더 파일은 프레임워크 안의 모든 헤더를 #import 한다.

이러한 접근 방법은 10여 년간 잘 동작했다.
그러나 더 많은 클래스들이 프레임워크에 추가되고 각 프로젝트에는 더 많은 프레임워크가 포함됐다.
결국 컴파일러는 동일한 표준 헤더를 계속해서 파싱하고 처리하는 데 대부분의 시간을 소비하게 됐다.


-
미리 컴파일된 헤더 파일(precompiled header file)이 모든 프로젝트에 추가됐다.
처음으로 프로젝트를 컴파일하면 그 파일에 나열된 헤더들은 한 번만 컴파일되고 그 결과는 캐시된다.
미리 헤더의 요약 정보를 가져 다른 모든 파일들을 더 많이 더 빨리 컴파일할 수 있게 된다.
.pch 파일이 precompiled-header file 이다.


-
이는 지난 10여 년 동안 잘 사용됐지만, 최근 애플은 개발자들이 .pch 파일을 효과적으로 관리하지 못한다는 사실을 깨달았다.
그래서 이를 효율적으로 사용하기 위해서 @import 가 도입되었다. ( @import Foundation; )
컴파일러에게 알아서 import 하라고 이야기 하는 것과 같다.
컴파일러는 헤더 파일의 전처리(preprocessing)와 캐싱(caching) 과정을 최적화하는 여러 선택사항을 가지고 있다.
현재는 오직 애플만이 @import 지시자를 사용해 모듈을 만들 수 있다.

자신이 만든 클래스나 프레임워크에서는 여전히 #import 지시자를 사용해야 한다.








반응형

댓글