
부스트코스 iOS 과정을 수료하면서 조언 받았던 내용을 정리합니다.
1. 프로퍼티/메서드를 private 로 선언 후 필요에 따라 Access Control 를 조정한다.
class ViewController: UIViewController { | |
private var player: AVAudioPlayer? // O | |
var timer: Timer? // X | |
... | |
} |
OOP 정보은닉과도 관련되어 있는 내용입니다. 소프트웨어 유연성을 늘리기 위해서는
객체간의 의존성을 낮추는 것이 중요한데요. 모든 프로퍼티/메서드가 Internal 로 설정되어
있다면, 어떤 부분이 외부에서 접근 가능한지 파악하기 어렵지만, 필요한 부분만
Access Control 를 조정한다면 개발자가 이를 파악하기 쉬운 장점이 있습니다.
2. Retain Sycle 을 방지하기 위해 Weak, Unowned 키워드를 적절하게 사용한다.
class ViewController: UIViewController { | |
@IBOutlet var playPauseButton: UIButton! | |
// Storyboard/Xib 에서 addSubView 가 되면서 | |
// 참조 카운트가 올라가기 때문에 weak 키워드로 선언해야 한다. | |
... | |
func makeAndFireTimer() { // escaping closure 로 순환참조가 발생할 수 있다. | |
timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { | |
[unowned self] (timer: Timer) in | |
// 따라서 capture list 에서 self 를 weak 로 선언해준다. | |
if self.progressSlider.isTracking { return } | |
self.updateTimeLabelText(currentTime: self.player?.currentTime) | |
self.progressSlider.value = Float(self.player?.currentTime ?? 0) | |
}) | |
timer?.fire() | |
} | |
} |
Swift 는 ARC 를 통해 메모리를 관리합니다. GC 보다 효율적이지만,
개발자의 오판에 의해 순환 참조(Retain Sycle) 를 발생시킬 수 있습니다.
참조 카운트가 올라간 인스턴스는 메모리에서 해제되지 않기 때문에
상황에 따라 Weak, Unowned 키워드를 적절하게 사용해야 합니다.
3. 메서드, 변수 네이밍을 컨벤션에 맞추어 작업한다.
@IBAction func ifTapView() { | |
... | |
} | |
// 위 경우에 viewTapped() 가 더 적합하다 | |
var setCity: City? | |
// 타입, 프로퍼티, 변수 및 상수 명은 모두 명사(구)로 지어야 한다. | |
// 따라서 set 으로 시작하는 위 예제는 올바르지 않다. | |
// var city: City? | |
var statusBarHidden: Bool? | |
// Boolean 타입의 메서드나 프로퍼티는 Assertion 과 같이 읽혀야 한다. | |
// 따라서 isStatusBarHidden 이 적합하다. |
위 내용의 경우 Swift Design GuideLines 에 자세하게 기술되어 있습니다.
나중에 협업을 위해 더 학습해야겠다고 느끼게 되었습니다. ( 코드 작성할 때마다 수시로 보기 )
4. 특정 이벤트에 사용되는 객체의 경우 lazy 키워드를 사용한다.
class RegisterMainViewController: UIViewController { | |
lazy var imagePicker: UIImagePickerController = { | |
let picker = UIImagePickerController() | |
picker.modalPresentationStyle = .fullScreen | |
picker.sourceType = .photoLibrary | |
picker.allowsEditing = true | |
picker.delegate = self | |
return picker | |
}() | |
... | |
} |
lazy 키워드로 선언된 변수는 사용되기 전까지 연산되지 않습니다.
위 경우에는 사용자가 버튼을 클릭했을 때에만 이미지 피커가 나오도록
구성하였는데요. 클릭하지 않았을 때에는 꼭 피커를 로드할 필요가
없기 때문에 lazy 키워드를 통해 메모리를 효율적으로 사용할 수 있습니다.
5. Identifier 를 static 변수로 설정하여 사용한다.
class CityCustomTableViewCell: UITableViewCell { | |
static let cityCellIdentifier: String = "cityCell" | |
... | |
} |
특정 storyboard 를 가져오거나, cell, xib 를 등록/로드 할 때 string 으로 되어 있는
Identifier 를 사용하는데요. 다른 곳에서 cell 를 재사용할 때 반복적으로 사용되는 경우가
많은데, 이럴 때 static 변수로 Indentifier 를 지정하면 실수를 줄일 수 있습니다.
( 유지보수 면에서도 좋음 )
6. 모델은 Class 일 필요가 없다면 Struct 로 설계하는 것을 고려해보자.
struct Album { | |
var fetchResult: PHFetchResult<PHAsset> | |
var asset: PHAssetCollection | |
init(fetchResult: PHFetchResult<PHAsset> = PHFetchResult(), asset: PHAssetCollection = PHAssetCollection()) { | |
self.fetchResult = fetchResult | |
self.asset = asset | |
} | |
... | |
mutating func changefetchOrder(option: OrderState) { | |
self.fetchResult = PHAsset.fetchAssets(in: asset, options: option.fetchOption) | |
} | |
} |
Struct 는 Value Type 으로 다음과 같은 장점을 가지고 있습니다.
1. Reference Type 에 비해서 성능이 좋다. ( Stack 에 생성되므로, 참조 카운트도 없다 )
2. 멀티쓰레드 작업시에 안전합니다. ( Immutable 이기 때문에 type-safe )
이글에 더 자세하게 설명이 되어있습니다. ( realm, swift-performance posting )
7. Swift 에서 제공하는 프로토콜을 활용한다.
Swift 는 다음과 같은 다양한 프로토콜들을 제공하고 있습니다.
널리 알려져 있는 Codable 프로토콜을 준수하면 쉽게
JSON 데이터를 디코딩/인코딩 할 수 있는데요.
이번 예제에서는 CaseIterable 에 대해서 다루겠습니다.
@IBAction func orderButtonTapped(_ sender: UIBarButtonItem) { | |
let alert = UIAlertController(title: "정렬방식 선택", message: "영화를 어떤 순서로 정렬할까요?", preferredStyle: .actionSheet) | |
let reservation = UIAlertAction(title: "예매율", style: .default, handler: { _ in | |
self.requestMovieData(orderOption: .reservation) | |
return | |
}) | |
let curation = UIAlertAction(title: "큐레이션", style: .default, handler: { _ in | |
self.requestMovieData(orderOption: .curation) | |
return | |
}) | |
let release = UIAlertAction(title: "개봉일", style: .default, handler: { _ in | |
self.requestMovieData(orderOption: .release) | |
return | |
}) | |
... | |
} |
버튼을 누르면 정렬 순서를 변경할 수 있는 Action Sheet 가 나오게 되는 코드입니다.
코드 자체로는 깔끔하다고 생각할 수 있지만, 프로젝트에서는 OrderType 이라는
열거형이 선언되어 있고, Model 에서는 OrderType 을 사용하고 있습니다.
이때 OrderType 이 변경될 경우, 모든 title 변경해야 하는 단점이 있습니다.
이런 상황에서 CaseIterable 이 유용하게 사용될 수 있습니다.
enum OrderType: CaseIterable { | |
case reservation | |
case curation | |
case release | |
var title: String { | |
switch self { | |
case .reservation: | |
return "예매율" | |
case .curation: | |
return "큐레이션" | |
case .release: | |
return "개봉일" | |
} | |
} | |
// ... | |
} |
@IBAction func orderButtonTapped(_ sender: UIBarButtonItem) { | |
let alert = UIAlertController(title: "정렬방식 선택", message: "영화를 어떤 순서로 정렬할까요?", preferredStyle: .actionSheet) | |
OrderType.allCases.forEach { type in | |
let action = UIAlertAction(title: type.title, style: .default, handler: { _ in | |
self.requestMovieData(orderOption: type) | |
}) | |
alert.addAction(action) | |
// ... | |
} |
위와 같이 enum 에 CaseIterable 프로토콜을 채택하면,
allCases 를 통해 모든 case 에 접근할 수 있습니다. 따라서 ForEach 를 통해
title 값을 설정하면 코드를 간결하게 할 수 있습니다.
'iOS Dev > Swift' 카테고리의 다른 글
Swift Reference Count 고찰 ( with side table ) (0) | 2021.10.08 |
---|---|
iOS JSON 을 Decoding 하는 여러 가지 방식들 (0) | 2021.08.26 |
Swift Optional ( enum, wrapped ) 정리 (0) | 2020.10.12 |
Swift ARC ( Automatic Reference Counting ) 정리 (2) | 2020.10.07 |
윈도우 10 에서 Swift 5 사용하기 ( Visual Code, WSL ) (4) | 2020.08.01 |