글

[Troubleshooting] tableViewCell에서 제약조건이 동작하지 않을 때

이미지
[Troubleshooting] tableViewCell에서 제약조건이 동작하지 않을 때 이번 포스트는 tableViewCell에서 제약조건이 동작하지 않아 고생했던 경험을 나누려고 합니다. 스토리보드에서 tableViewCell안에 여러개의 view를 추가한 다음에 제약조건을 작성했습니다. 그다음 앱을 실행해서 확인해 보았는데 제약조건이 전혀 동작하지 않았습니다. 원인은 tableViewCell의 contentView에 CustomView를 설정해서 제약조건이 동작하지 않았습니다. 왜 contentView를 사용자화하면 문제가 발생하는지 궁금했습니다. 원인을 파악하기 위해서 그날의 상황을 가정하여 간단한 프로젝트를 만들어 재현해보겠습니다. 먼저, 아래 그림처럼 스토리보드를 구성했습니다. 메인화면에서 move 버튼을 선택하면 tableView가 보이도록 단순하게 되어 있습니다. TableViewController는 TestTableViewController를 만들어 설정했고 cell은 TestTableViewCell로 만들었습니다. contentView는 TestView로 설정하고 TestTableViewCell에 titlte property를 만들어서 스토리보드에 연결시켰습니다. 제약 조건은 아래처럼 설정했습니다. TestTableViewController는 아래 코드처럼 3개의 항목이 보이도록 작성했습니다. class TestTableViewController : UITableViewController { let datas = [ "1. Test" , "2. Test" , "3. Test" ] override func numberOfSections ( in tableView : UITableView ) - > Int { return 1 } overri...

dismiss에 대해서 알아봅시다

이미지
dismiss에 대해서 알아봅시다 예전에는 dismiss를 호출하면 호출한 viewController가 dismiss된다고 생각했습니다. 그런데 어떤 경우는 자신이 dismiss되는게 아니라 자신이 present한 viewController가 dismiss되었습니다. dismiss 동작에 대해서 제대로 이해하지 못해서 벌어진 일이었습니다. 그래서 이번에는 dismiss에 대해서 알아보려고 합니다. 먼저, Apple API에 정의된 dismiss를 먼저 알아보겠습니다. presenting viewController는 presented viewController를 dismiss하는 책임을 가지고 있습니다. 만약 presented viewController에서 dismiss를 호출한 경우는 UIKit이 presenting viewController에 해제 관리를 요청합니다. 여기서 말하는 presentingViewController와 presentedViewController는 무슨 객체일가요? presentingViewController객체는 자신을 화면에 보이도록 만든 viewController입니다. presentedViewController는 자신이 화면에 보이도록 표시한 viewController입니다. 아래 코드로 presentingViewController와 presentedViewController를 설명하겠습니다. let viewController1 = UIViewController ( ) let viewController2 = UIViewController ( ) let viewController3 = UIViewController ( ) viewController 3개가 존재하는데 viewController1이 화면에 표시되고 있는 rootViewController라고 가정하겠습니다. viewController1 . present ( viewController2 , ...

thread와 dispatch queue와의 관계

이미지
thread와 dispatch queue와의 관계 test case를 작성하던 중 수행하는 코드가 main thread에서 동작하는지 확인해야하는 상황이 있었습니다. 그 상황을 만들기 위해서 global queue에서 main queue를 동작시켜 해당 코드가 main thread인지를 확인하는 코드를 작성했습니다. 그런데 그 test case는 통과하지 못하고 계속 실패했습니다. global queue가 동작한 thread는 background thread였는데 그 곳에서 dispatch_sync로 main queue를 동작시키면 main thread가 아닌 background thread에서 main queue를 동작시켜서 문제가 발생했습니다. main queue는 무조건 main thread에서 동작한다고 알고 있어서 dispatch로 하면 당연히 main thread라고 생각했습니다. 그런데 sync로 동작할 때는 제 예상하고 달랐습니다. test case에서 async로 동작을 시켰더니 test case를 통과했습니다. sync와 async 사이에 차이가 단순히 동기화만의 문제가 아닌 다른 차이가 있다는 생각이 들던찰라에 xcode 업데이트 후에 다시 위의 상황에서 dispatch_sync로 main_queue를 실행했더니 이번에는 main thread에서 동작했습니다. Xcode버그이거나 simulator가 오작동을 일으켰던 것 같습니다. 이번일을 겪고 나서 dispatch_queue와 thread의 관계에 대해서 알아보고 싶어서 이 글을 쓰게 되었습니다. 먼저, thread에 대해서 알아보겠습니다. thread는 어플리케이션에서 다른 실행경로를 만드는 역할을 합니다. 프로세스에 할당된 메모리를 공유하며 여러개의 thread가 동시에 실행될 수 도 있습니다. 프로세스와 다른 점은 프로세스 끼리는 메모리를 공유하지 않지만 같은 프로세스 안에 있는 thread끼리는 메모리를 공유합니다. 한 프로세스는 여...

unowned vs unowned(safe) vs unowned(unsafe)

unowned vs unowned(safe) vs unowned(unsafe) unowned를 사용하는 방법은 아래처럼 총 세가지가 있습니다. unowned unowned(safe) unowned(unsafe) 대부분 unowned만 사용하며 나머지는 크게 사용할 일이 없습니다. 그렇다면 나머지 2개는 왜 존재할가요? 우선은 각각에 대해 먼저 알아보겠습니다. unowned(safe) unowned(safe)는 참조를 사용할 때 런타임 safety checker가 먼저 검사를 수행합니다. 런타임 safety checker는 사용하는 메모리가 실제 사용하는 객체가 맞는지를 확인합니다. 참조한 객체가 해제가 되어 더이상 메모리가 유효한 객체가 아니라면 런타임 safety checker에 의해 즉시 런타임 오류가 발생합니다. unowned(unsafe) unowned(safe)는 참조를 사용할 때 런타임 safety checker가 비활성화됩니다. 참조한 객체가 해제된 후에 사용하면 대부분 런타임 오류가 발생하지만 발생하지 않고 정상동작이나 엉뚱한 동작을 할 수 있습니다. 런타임 safety checker에 의해 메모리를 조사하지 않기 때문에 해당 주소가 동일한 클래스의 객체로 변경되면 그 객체에서 정상동작을 할 수 있으며 다른 객체로 메모리가 교체되었다면 예상하지 못한 엉뚱한 동작을 할 수도 있습니다. 그래서 안전하지 않다고 표현하는 것입니다. unowned(unsafe)를 사용하려면 사용하는 사람이 안전하게 동작하도록 모든 책임을 가져야합니다. unowned unowned는 대부분의 경우 unowned(safe)와 동일하게 동작을 합니다. 다만, 컴파일할 때 옵션에 -Ofast를 설정하면 unowned(unsafe)로 동작합니다. Int가 32 bit에서는 4byte로 동작하고 64bit에서는 8byte로 동작하는 개념과 비슷하다고 할 수 있습니다. 각각에 대해서 알아보았습니다. 이제 un...

weak와 unowned의 차이는?

weak와 unowned의 차이는? 메모리 참조에 weak와 unowned 참조가 있습니다. weak와 unowned는 모두 class타입에서만 사용할 수 있으며 메모리의 소유를 주장하지 않습니다. weak 참조 먼저, weak에 대해서 알아보겠습니다. weak 참조는 메모리의 retain count를 증가시키지 않고 객체를 저장합니다. retain count를 증가시키지 않기 때문에 순환참조 문제를 방지할 수 있습니다. 참조한 메모리가 해제되었을 때는 ARC에 의해 자동적으로 nil로 초기화됩니다. 따라서, dangling pointer가 되는 것을 걱정하지 않아도 됩니다. 사용법은 변수 키워드 앞에 weak를 붙이면 됩니다. weak는 nil로 초기화될 수 있기 때문에 Optional type만 가능합니다. 당연히 let이 아닌 var로 선언해야합니다. weak var reference : Optional type weak 참조는 어디에서 사용할 수 있을 가요? 바로 순환 참조가 발생할 수 있는 참조나 delegate, closure에서 사용할 수 있습니다. class Account { var bank : Bank ? deinit { print ( "deinit Accout" ) } } class Bank { var account : Account ? deinit { print ( "deinit Bank" ) } } var account : Account ? = Account ( ) // account retainCount: 1 var bank : Bank ? = Bank ( ) // bank retainCount: 1 account ? . bank = bank ...

closure에서는 왜 self를 사용해야 할까?

이미지
closure에서는 왜 self를 사용해야 할까? class에서 closure를 사용할 때 closure 안에서 객체의 변수 또는 함수에 접근할 때 self를 붙여서 사용을 해야합니다. class안에서는 self없이 변수 및 함수를 사용할 수 있고 로컬변수도 바로 사용할 수 있는데 왜 closure에서는 self를 붙여야 할가 궁금했습니다. objective-c에서도블럭안에서 self를 명시적으로 사용해야합니다. 물론, self없이 사용은 가능하지만 warning 메시지가 발생합니다. 이 부분을 이해하기 위해서는 먼저 capture list에 대해서 알아야합니다. capure list는 closure 안에서 사용할 객체를 정의하는 목록입니다. 문법 구조는 아래 와 같습니다. let closure: (Bool)->(T) = { [capture list 작성 ] (parameter) in // closure body } capture list는 여러개 정의가 가능하고 콤마(,)로 구분합니다. 정의한 항목이 class인 경우 ‘weak’ 또는 'unowned’를 함께 사용할 수 있습니다. 예를 들면, 아래 코드처럼 작성할 수 있습니다. func testCaptureList ( ) { let aString1 : String = "capture1" let aString2 : String = "capture2" let closure : ( Bool ) - > ( ) = { [ capture1 = aString1 , capture2 = aString2 ] ( success : Bool ) in print ( capture1 , capture2 ) } } aString1은 capture1에 할당하고 aString2은 capture2에 할당했습니다. ...

swift init 상속에 대해서 알아봅시다.

이미지
swift init 상속에 대해서 알아봅시다. swift를 처음 접했을 때 init의 상속관련해서 어떤 경우에는 상속이 이루어지고 어떤 경우에는 상속이 되지않아서 이해하기 어려운 부분이 있었습니다. 이번 기회에 init의 상속 관련 내용을 정리하려고 합니다. 먼저, 지정 초기화(designated initializer)와 편의 초기화(convenience initializer)의 관계를 정리하면 3가지 법칙이 있습니다. 지정 초기화는 부모의 지정 초기화를 호출해야합니다. 편의 초기화는 부모가 아닌 자신의 초기화를 호출해야합니다. 편의 초기화는 마지막 지점에서는 지정 초기화를 호출해야합니다. 간단히 다시 정리하면 지정 초기화는 부모의 지정 초기화에게만 위임을 할 수 있으며, 편의 초기화는 자신의 편의 초기화 또는 지정 초기화에게만 위임할 수 있습니다. 아래 이미지로 설명하면 지정 초기화는 위 방향으로만 위임하고 있으며, 편의 초기화는 옆 방향으로 위임하고 있습니다. 아래 이미지는 좀더 복잡한 경우이며 아래의 경우도 위에 설명드린 3가지룰을 지키고 있습니다. 다음으로 알아볼 사항은 property 초기화입니다. swift는 두 단계 초기화로 이루어져 있습니다. 첫번째 단계에서는 property를 초기화를 수행합니다. 먼저 자신의 property를 모두 초기화하고 그 다음 부모의 property를 초기화합니다. 모든 property를 초기화하고 나면 두번째 단계를 진행합니다. 두번째는 저장된 property를 조작할 기회를 부여합니다. 이때는 자신이 아닌 부모부터 기회를 부여받습니다. objective-c와 비교하면 swift 초기화 방식이 다르다고 생각할 수 있습니다. objective-c의 초기화 함수를 보면 부모부터 진행하고 자기 자신의 초기화 코드가 가장 나중에 진행되기 때문입니다. 사실 objective-c의 초기화 단계도 swift의 초기화처럼 두단계로 구성되어 있습니다. 다만, obj...