• Xcode의 기본 컴파일러: LLVM(Low Level Virtual Machine)
    정적 분석기(Static Analyzer) : 애플의 LLVM 컴파일러 기술을 이용해 코드 분석 및 버그 찾음
  • Objective-C는 nil 객체에 메시지를 보내어도 죽지 않는다 (다만 실행이 안될뿐)
  • 지정된 초기화 메서드(Designated Initializer): 사용자 정의 초기화 메서드

 

 

NSObject

- (id)init : receiver 초기화
- (BOOL)isEqual:(id)anObject : 포인터 비교

 

  • Cocoa 클래스들의 초기화 메서드는 초기화 불가능하다면 nil return
- (id)init {
    self = [super init];
    
    if (self) {
        ...
    }
} 

 

 

 

 

isa: 객체를 생성하는 클래스 구조체를 가리키는 포인터

  • Objective-c 메시지 동작 방식: NSObject에는 isa라는 인스턴스 변수가 존재
  • 클래스 구조체
    - 클래스의 인스턴스 변수 유형과 이름
    - 클래스 메서드의 구현
    - 슈퍼클래스의 클래스 구조체를 가리키는 포인터

 

 

Selector : 클래스의 메서드 찾기

  • 각 메서드 이름 - 유일한(unique) 정수로 연결
    ex) [application respondsToSelector:@selector(registerUserNotificationSettings:)]

  • 컴파일할 때 컴파일러는 메시지를 보내야하는 곳에서 selector를 연결

  • 동작 방식

 

[myObject addObject:yourObject]; 
=> objc_msgSend(myobject, 12, yourObject);    // (addObject:의 selector는 12라고 가정)

1) objc_msgSend()는 myObject의 isa 포인터에서 클래스 구조체를 가져옴
2) 클래스 구조체에서 12에 연결된 메서드 찾음 (없다면 슈퍼클래스로 가서 찾고 아예없으면 예외 처리)

 

objective-c 데이터 구조 중 셀렉터를 메서드 이름으로 연결한 표가 있음 

 

The App Life Cycle

  • 앱은 우리가 작성한 코드와 System Framework 간의 상호작용의 결과물
  • Framework에서는 앱의 실행에 필요한 환경에 필요한 도구를 제공
  • 이러한 Framework를 효과적으로 사용하기 위해서 iOS Infra structure에 대한 간단한 이해가 필요
  • iOS Framework는 MVC와 Delegation이라는 디자인패턴에 의존

 

The Main Function

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
 
int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

UIApplicationMain 함수: 앱이 시작되면서 몇가지 중요한 객체 생성하고 앱 실행

UIApplication: 시스템과 앱의 여러 객체들간의 대화를 가능하게 해주는 객체

MVC 구조는 앱의 data와 비지니스 로직을 UI 요소로 분리해줌.

 

 

The Structure of an App

  • UIApplication 객체: 
    Event loop 관리. Delegate에 앱 상태변화나 푸쉬알림같은 주요한 이벤트 알려줌.
  • App Delegate 객체:
    delegate. 이 객체는 UIApplication 객체와 함께 앱 초기화, 앱 상태변화 등 관리. 앱 하나당 하나만 존재
  • Document, data model 객체: 
    data model은 앱의 콘텐츠 저장, 앱의 고유성을 가짐
  • View Controller 객체
    - 내용을 화면에 나타내는 기능 (하나의 view와 subview들을 관리)
    - 화면에 view가 표시될 때 viewcontroller 객체는 뷰들을 앱의 window에 설치
    - UIViewController
       : 모든 view controller의 부모
       : View 로드하고, 보여주고, 디바이스 회전에 따라 돌려주는 등에 대한 기능 제공
  • UIWindow 객체
    - 화면에 나타낼 뷰들을 다룸 (대부분 Main Screen에 해당하는 window하나만 가짐)
    - UIApplication 객체와 같이 작동하여 view controller와 view에게 이벤트 전달하는 역할
  • view 객체, control 객체, layer 객체
    - 앱 컨텐츠 중 시각적인 부분 제공
    - View: frame(지정된 사각형)의 공간에 내용물, 그 프레임 안의 이벤트에 응답하는 객체
    - Control: 특정한 역할을 담당하는 view (Button, Text field, toggle, switch)
    - Layer: 시각적인 요소에 대한 Data 객체 (view는 screen의 뒷부분에서 이 layer객체를 사용하여 컨텐츠 내용을 나타냄. custom layer 객체를 사용하여 애니메이션을 줄 수 있음.)

 

The Main Run Loop

  • UIApplication 객체는 앱이 launch되는 시점에 메인 run loop를 생성한 뒤 이 run loop로 이벤트 처리
  • Main Thread에서 동작
  • 사용자 관련 이벤트를 받은 순서대로 처리
  • 사용자 액션
    -> 액션에 대한 이벤트가 시스템에 의해 생성
    -> UIKit에서 생성한 port 를 통해 앱에 전달
    -> EventQueue에 저장된 순서대로 동작 실행 (UIApplication 객체가 이벤트를 받아서 어떤 동작을 취할지 결정)

    ex) touch event: main window 객체가 인식 -> window 객체가 다시 터치가 발생한 view로 이벤트 전달

 

이벤트 종류

  • Touch: View는 응답을 할 줄 아는 객체. 해당 뷰에서 처리되지 않는 터치 이벤트는 Responder chain에 다라 계속 내려감
  • Remote Control Shake motion events
  • Accelerametor, Magnetometer, Gyroscope
  • Location
  • Redraw

- touch, remote control과 같은 이벤트는 앱의 Responder 객체를 통해 처리됨. (Responder - UIApplication 객체, view 객체, view controller 객체 ...)

- Control에서 발생한 터치 이벤트는 다른 view의 이벤트 전달 방식과 조금 다름.
   그 이벤트들을 다시 action 메시지로 패키징하여 지정된 객체로 전달 => Target-action 디자인 패턴: 특정 액션이 작성한 코드로 연결되는 작업을 쉽게 해주는 패턴

 

 

Execution States for Apps

앱은 항상 아래 중 하나의 상태를 가지고, 시스템 내에서 일어나는 액션들을 통해 시스템은 앱의 상태를 바꿈

 

  • Not running: 실행되지 않음, 시스템에 의해 종료
  • Inactive: 앱이 foreground에 올라와 있지만 이벤트를 받지 않고 있는 상태
    (보통의 앱은 다른 앱으로 전환할떄 잠시동안 이상태에 머물게된다.)
  • Active: 앱이 foreground에서 실행중이고 이벤트를 받고 있는 상태
  • Background: 앱이 background에 있고 코드를 실행하고 있는 상태.
    (맵이 직접 background로 실행될땐, Inactive상태가 아니라 바로 이상태로 머문다)
  • Suspended: 앱이 background에 있으면서 코드를 수행하고 있지 않는 상태.
    (시스템은 자동적으로 앱을 이상태로 바꾼다. 이상태일때는 메모리에 앱이 남지만 어떠한 코드도 실행되지않는다. 하지만 메모리가 부족하면 알림없이 메모리공간을 차지하기위해 이상태인 앱을 메모리에서 제거한다. 그래서 오랜시간 켜지않은 앱을 다시 불러오면 처음부터 시작되는 이유)

 

 

상태 변화는 AppDelegate 객체의 메소드 호출

- application:willFinishLaunchingWithOptions: 앱 최초 실행

- application:didFinishLaunchingWithOptions: 앱의 화면이 사용자에게 보여지기 직전 최종 초기화 작업

- applicationDidBecomeActive: 앱이 foreground로 전환

- applicationWillResignActive: 앱이 foreground에서 다른 상태로 전환

- applicationDidEnterBackground: background

- applicationWillEnterForeground: foreground

- applicationWillTerminate: 앱 종료

 

 

Thread and Concurrency

iOS의 Multi-thread 를 위해서는 Grand Central Dispatch, Operation Objects, 비동기 progrmaiing interfaces

  • View 관련 작업은 Main Thread
  • 규모가 큰 작업은 GCD 또는 operation 객체를 통해 background thread에서 이루어지도록 하자.

 

Ref.

https://llldddjjj.tistory.com/entry/iOS-iOS-%EC%95%B1%EC%9D%84-%EA%B5%AC%EC%84%B1%ED%95%98%EB%8A%94-%ED%95%B5%EC%8B%AC-%EA%B0%9D%EC%B2%B4%EB%93%A4%EA%B3%BC-iOS-%EB%A9%94%EC%9D%B8-%EB%9F%B0%EB%A3%A8%ED%94%84-%EB%8F%99%EC%9E%91-%EC%9D%B4%ED%95%B4

http://rhammer.tistory.com/94
https://github.com/MijeongJeon/iOS-Programming-Documents/blob/master/App%20Programing%20Guide%20for%20iOS(Korean).md

All top-level classes are, by definition, static.

What the static boils down to is that an instance of the class can stand on its own. Or, the other way around: a non-static inner class (= instance inner class) cannot exist without an instance of the outer class. Since a top-level class does not have an outer class, it can't be anything but static.

Because all top-level classes are static, having the static keyword in a top-level class definition is pointless.

Some code to play around with:

public class Foo {

    public class Bar {
         // Non-static innner class
    }

    public static class Baz {
         // Static inner class
    }
}

public class Example {
    public static void main(String[] args) {
        new Foo(); // this is ok
        new Foo.Baz(); // this is ok
        new Foo.Bar(); // does not compile!

        Foo f = new Foo();
        Foo.Bar bar = f.new Bar(); //this works, but don't do this
    }
}

 

I put the "but don't do this" in there because it's really ugly code design. Instance inner classes should not be visible outside the outer class. They should only be used from within the outer class.

 

 

Inner class는 그 안에서만 사용. 그러니까 inner로 만들었자나아아아

Top level class는 static 사용 못함.

 

 

 

 

 

Ref. https://stackoverflow.com/a/7370832

분할 정복법 (Divide and Conquer)

아래 3가지 단계를 거쳐서 어떤 문제를 해결하는 기법

Merge Sort와 Quick Sort에서 사용하는 기법

  • 분할(Divide) : 해결하고자 하는 문제를 작은 크기의 동일한 문제들로 분할
  • 정복(Conquer) : 각각의 작은 문제를 순환적으로 해결
  • 합병(Merge): 작은 문제의 해를 합하여(merge) 원래 문제에 대한 해를 구함

Merge Sort

Step 1) Divide - 데이터가 저장된 배열을 절반으로 나눔

Step 2) Recursively sort - 각각을 순환적으로 정렬

Step 3) Merge - 정렬된 두 개의 배열을 합쳐 전체를 정렬

Step 3인 Merge 과정 자세히 보기 

- 정렬된 두 배열을 Merge하는 과정

- 새로운 추가 배열 필요

- i(첫번째 list에서 가장 작은 값), j(두번째 list에서 가장 작은 값), k(추가 list에서 가장 작은 값) 변수 필요

 

1. A[i]와 A[j]를 비교하여 작은 값을 추가 배열에 추가

2. 작은 값에 속한 인덱스 올림. i++ or j++ , k++

3. i or j가 해당 배열의 인덱스보다 크다면 다른 배열의 값은 그대로 추가배열에 추가 해주면 됨!

소스

// 재귀 함수이므로 매개변수 명시화 필요
public void mergeSort(int[] arr, int p, int r) {		
	if (p < r) {
		int q = (p+r) / 2;		// 1. p와 r의 중간 지점 계산
		mergeSort(arr, p, q);	// 2. 전반부 정렬
		mergeSort(arr, q+1, r);	// 3. 후반부 정렬
		merge(arr, p, q, r);	// 4. 합병
	}
}

/* Merge Business Logic - 정렬되어 있는 두 배열 arr[p...q]와 arr[q+1...r]을 합하여 
 * 정렬된 하나의 배열 arr[p...r]을 만듦. 
 * arr[p... q] arr[q+1...r] => 정렬되어 있는 두 배열 
 * arr[p.................r] => 정렬된 하나의 배열을 만드는 작업 : Merge
 */
public void merge(int[] arr, int p, int q, int r) {
	int i=p, j=q+1, k=p;
	int[] temp = new int[arr.length];
	
	// 각각 i, j가 각 배열의 길이를 넘지 않는다면 비교하여 temp에 넣어주기 
	while(i <= q && j <= r) {
		if (arr[i] <= arr[j]) {
			temp[k++] = arr[i++];
		} else {
			temp[k++] = arr[j++];
		} 
	}
	
	// 첫번째 배열에 있는 나머지 값들은 이미 정렬된 데이터들이므로 차례대로 temp 배열에 넣어주면 됨. 
	while (i <= q) {
		temp[k++] = arr[i++];
	}
	
	// 두번째 배열에 있는 나머지 값들은 이미 정렬된 데이터들이므로 차례대로 temp 배열에 넣어주면 됨. 		
	while(j <= r) {
		temp[k++] = arr[j++];
	}
	
	// index p부터 r까지 원래 배열에 값 넣기 
	for (int l = p; l <= r; l++) {
		arr[l] = temp[l];
	}
	
	print(arr);
}

 

시간복잡도 T(n) = O(nlogn)

if n = 1      -> 0

otherwise -> T(n/2) + T(n/2) + n = 2T(n/2) + n

 

Merge sort 시간복잡도

 

 

 

 

 

 

Ref. https://www.youtube.com/watch?v=2YvFRAC8UTM

Insertion Sort(삽입 정렬) 란?

아직 정렬되지 않은 임의의 데이터를 이미 정렬된 부분의 적절한 위치에 삽입해 가며 정렬하는 방식

Insertion 단계

- item to insert 전의 데이터들은 정렬이 되있는 상태

- item to insert를 정렬 데이터들의 뒤부터 비교하는 것이 효율적! -> 배열에 저장되어있기 때문

Insertion

public void insertSort(int[] arr) {
	if (arr.length == 0) {
		return;
	}
	
	System.out.print("[정렬할 원소] ");
	print(arr);
	
	for (int i=1; i<arr.length; i++) {		// 1
		for (int j=i-1; j>=0; j++) {
			int temp = arr[i];
			
			// 2
			if (arr[i] > arr[j]) {
				arr[j+1] = temp;
				break;
			}
			
			arr[j+1] = arr[j];		
		}
		

		System.out.print(i + "단계 : ");
		print(arr);
	}
}

 

실행시간

1번의 for 루프는 n-1번 반복

2번의 삽입은 최악의 경우 i-1번 비교

 

최악의 경우 시간복잡도:  T(n) = (n-1)+(n-2)+...+2+1 = O(n^2)

 

 

 

 


Ref. https://www.youtube.com/watch?v=0dG7xTt5IfQ

서로 이웃한 데이터들을 비교하여 가장 큰 데이터를 가장 뒤에 보내며 정렬하는 방식

가장 큰 값을 마지막으로 보내는 개념은 selection sort와 유사

 

Bubble Sort

public void bubbleSort(int[] arr) {
	if (arr.length == 0) {
		return;
	}
	
	System.out.print("[정렬할 원소] ");
	print(arr);
	
    	// 뒤에서부터 정렬하므로 last pointer는 정렬된 데이터 앞을 가리킴
	for (int last=arr.length-1; last>=0; last--) {	 // 1
		for (int i = 0; i <= last-1; i++) {	 // 2
			if (arr[i] > arr[i+1]) {
				swap(arr, i, i+1);	 // 3
			}
		}
		
		System.out.print(arr.length - last + "단계 : ");
		print(arr);
	}
}

 

실행시간

1번의 for 루프는 n-1번 반복

2번의 for 루프는 각각 n-1, n-2, ..., 2, 1번 반복

3번의 교환은 상수 시간 작업

 

시간복잡도:  T(n) = (n-1)+(n-2)+...+2+1 = O(n^2)

 

 

 

 

 

Ref. https://www.youtube.com/watch?v=0dG7xTt5IfQ

배열 중 가장 큰 원소를 오른쪽부터 채워가며 숫자를 정렬하는 방법

 

각 루프마다

- 최대 원소 찾기

- 최대 원소 <-> 맨 오른쪽 원소 교환

- 맨 오른쪽 원소 제외

Selection sort

public void selectionSort(int[] arr) {
	if (arr.length == 0 ) {
		return;
	}
	
    	System.out.print("[정렬할 원소] ");
	print(arr);
    
	int max;
	
	for (int last = arr.length-1; last>0; last--) { // 1
		max = last - 1;
		
		// 마지막을 제외한 나머지 element 중 가장 큰 수 찾기
		for (int k = last-1; k >= 0; k--) {		// 2
			if (arr[k] > arr[max]) {
				max = k;
			}
		}
		
		if (arr[max] > arr[last] ) {	
			swap(arr, max, last);		// 3
		}
		
		System.out.print(arr.length - last + "단계 : ");
		print(arr);
	}

}

 

실행 시간

1번의 for 루프 n-1 번 반복

2번에서 가장 큰 수를 찾기 위한 비교 횟수: n-1, n-2, ..., 2, 1

3번의 교환은 상수시간 작업

 

시간 복잡도: T(n) = (n-1)+(n-2)+...+2+1 = n(n-1)/2 = O(n^2)

 

 

 

 

 

Ref. https://www.youtube.com/watch?v=0dG7xTt5IfQ

트리거(Trigger) 란? 

  • Triggering Event, 즉 Oracle DML 문인 INSERT, UPDATE, DELETE문이 TABLE에 대해 행해질 때 자동으로 수행되는 PROCEDURE
  • 행 트리거
    - 컬럼의 각각의 행의 데이터 행 변화가 생길때마다 실행
    - 실제 그 데이터 행의 실제값 제어가능
    - :old, :new
  • 문장 트리거
    - 트리거 사건에 의해 단 한번 실행되며, 컬럼의 각 데이터 행을 제어 할 수 없음
    - 컬럼의 데이터 값에 관계없이 컬럼에 변화가 일어남을 감지하여 실행되는 트리거
  • 문법

CREATE [OR REPLACE] TRIGGER trigger_name

BEFORE | AFTER

     trigger_event ON table_name

     [FOR EACH ROW]

     [WHEN (condition)]

     PL/SQL block

 

- BEFORE : INSERT, UPDATE, DELETE문이 실행되기 전에 트리거가 실행

- AFTER : INSERT, UPDATE, DELETE문이 실행된 후 트리거가 실행

- trigger_event : INSERT, UPDATE, DELETE 중에서 한 개 이상 올 수 있음

- FOR EACH ROW : 이 옵션이 있으면 행 트리거가 됨

 

  • Procedure와 Trigger의 차이점
Procedure Trigger
생성하면 소스코드와 실행코드(P-CODE)가 생성됨 생성하면 소스코드와 실행코드(P-CODE)가 생성됨
EXECUTE 명령어로 실행 생성 후 자동 실행
COMMIT, ROLLBACK 실행 가능 COMMIT, ROLLBACK 실행 안됨
  • 소스 예제
CREATE OR REPLACE TRIGGER Emp_Sal_Change
BEFORE
INSERT OR UPDATE ON emp
FOR EACH ROW
  WHEN (new.empno > 0)
  DECLARE
    sal_diff number;
BEGIN
  sal_diff := :new.sal - :old.sal;
  DBMS_OUTPUT.PUT_LINE('이전 급여 : ' ||:old.sal);
  DBMS_OUTPUT.PUT_LINE('신 급여 : ' || :new.sal);
  DBMS_OUTPUT.PUT_LINE('급여 차액 : '|| sal_diff);
END;
-- Table 추적 trigger
CREATE OR REPLACE TRIGGER emp_row_aud
  AFTER insert OR update OR delete ON emp
  FOR EACH ROW
BEGIN
  IF INSERTING THEN
    INSERT INTO emp_row_audit
      VALUES(emp_row_seq.NEXTVAL, :new.ename, 'inserting', SYSDATE);
  ELSIF UPDATING THEN
    INSERT INTO emp_row_audit
      VALUES(emp_row_seq.NEXTVAL, :old.ename, 'updating', SYSDATE);
  ELSIF DELETING THEN
    INSERT INTO emp_row_audit
      VALUES(emp_row_seq.NEXTVAL, :old.ename, 'deleting', SYSDATE);
  END IF;
END;

 

 

Ref. http://www.gurubee.net/lecture/1076

+ Recent posts