이렇듯 NSLock()으로 lock을 걸면 시간 간격 차이를 두는 것과 같은 출력값이 나오게 됩니다.
순서는 이렇습니다.
Thread 1 buy() 실행, lock() -> 다른 스레드에서 접근 못함 -> Thread 2 buy() 대기 -> Thread 1 buy() 실행 후 unlock() -> 다른 스레드에서 접근 가능 -> Thread 2 buy() 실행, lock() -> 다른 스레드에서 접근 못함 -> Thread 2 buy() 실행 후 unlock()
다른 스레드 사용 방지
unlock으로 잠금해제를 하지 않게 된다면? 다른 스레드에서 접근을 하지 못한채로 끝나게 되겠죠.
import UIKit
class TheaterCell: UITableViewCell {
// 상영관명
@IBOutlet var name: UILabel!
// 연락처
@IBOutlet var tel: UILabel!
// 주소
@IBOutlet var addr: UILabel!
}
TheaterListController 클래스
API를 호출하고 영화관 정보를 읽어온다.
import UIKit
class TheaterListController: UITableViewController {
// API를 통해 불러온 데이터를 저장할 배열 변수
var list = [NSDictionary]()
// 읽어올 데이터의 시작위치
var startPoint = 0
override func viewDidLoad() {
// API를 호출한다.
self.callTheaterAPI()
}
func callTheaterAPI() {
// 1. URL을 구성하기 위한 상수값을 선언한다.
let requestURI = "http://swiftapi.rubypaper.co.kr:2029/theater/list" // API 요청 주소
let sList = 100 // 불러올 데이터 개수
let type = "json" // 데이터 형식
// 2. 인자값들을 모아 URL 객체로 정의한다.
let urlObj = URL(string: "\(requestURI)?s_page=\(self.startPoint)&s_list=\(sList)&type=\(type)")
do {
// 3. NSString 객체를 이용하여 API를 호출하고 그 결과값을 인코딩된 문자열로 받아온다.
let stringdata = try NSString(contentsOf: urlObj!, encoding: 0x80_000_422)
// 4. 문자열로 받은 데이터를 UTF-8로 인코딩처리한 Data로 변환한다.
let encdata = stringdata.data(using: String.Encoding.utf8.rawValue)
do {
// 5. Data 객체를 파싱하여 NSArray 객체로 변환한다.
let apiArray = try JSONSerialization.jsonObject(with: encdata!, options: []) as? NSArray
// 6. 읽어온 데이터를 순회하면서 self.list 배열에 추가한다
for obj in apiArray! {
self.list.append(obj as! NSDictionary)
}
} catch {
// 경고창 형식으로 오류 메시지를 표시해준다.
let alert = UIAlertController(title: "실패",
message: "데이터 분석이 실패하였습니다",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .cancel))
self.present(alert, animated: false)
}
// 7. 읽어와야 할 다음 페이지의 데이터 시작 위치를 구해 저장해둔다.
self.startPoint += sList
} catch {
// 경고창 형식으로 오류 메시지를 표시해준다.
let alert = UIAlertController(title: "실패",
message: "데이터를 불러오는데 실패하였습니다",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .cancel))
self.present(alert, animated: false)
}
}
}
테이블 목록 구현
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.list.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// self.list 배열에서 행에 맞는 데이터를 꺼냄
let obj = self.list[indexPath.row]
// 재사용 큐로부터 tCell 식별자에 맞는 셀 객체를 전달받음
let cell = tableView.dequeueReusableCell(withIdentifier: "tCell") as! TheaterCell
cell.name?.text = obj["상영관명"] as? String
cell.tel?.text = obj["연락처"] as? String
cell.addr?.text = obj["소재지도로명주소"] as? String
return cell
}
/**
문제 설명
프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.
또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.
먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.
제한 사항
작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.
작업 진도는 100 미만의 자연수입니다.
작업 속도는 100 이하의 자연수입니다.
배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.
입출력 예
progresses speeds return
[93, 30, 55] [1, 30, 5] [2, 1]
[95, 90, 99, 99, 80, 99] [1, 1, 1, 1, 1, 1] [1, 3, 2]
입출력 예 설명
입출력 예 #1
첫 번째 기능은 93% 완료되어 있고 하루에 1%씩 작업이 가능하므로 7일간 작업 후 배포가 가능합니다.
두 번째 기능은 30%가 완료되어 있고 하루에 30%씩 작업이 가능하므로 3일간 작업 후 배포가 가능합니다. 하지만 이전 첫 번째 기능이 아직 완성된 상태가 아니기 때문에 첫 번째 기능이 배포되는 7일째 배포됩니다.
세 번째 기능은 55%가 완료되어 있고 하루에 5%씩 작업이 가능하므로 9일간 작업 후 배포가 가능합니다.
따라서 7일째에 2개의 기능, 9일째에 1개의 기능이 배포됩니다.
입출력 예 #2
모든 기능이 하루에 1%씩 작업이 가능하므로, 작업이 끝나기까지 남은 일수는 각각 5일, 10일, 1일, 1일, 20일, 1일입니다. 어떤 기능이 먼저 완성되었더라도 앞에 있는 모든 기능이 완성되지 않으면 배포가 불가능합니다.
따라서 5일째에 1개의 기능, 10일째에 3개의 기능, 20일째에 2개의 기능이 배포됩니다.
*/
- 제출한 답안
import Foundation
func solution(_ progresses:[Int], _ speeds:[Int]) -> [Int] {
var completionDate = [Int]()
for (i, value) in progresses.enumerated() {
completionDate.append((100 - value) / speeds[i])
if (100 - value) % speeds[i] != 0 {
completionDate[i] += 1
}
}
var release = [Int]()
var releases = [[Int]]()
for date in completionDate {
if date > completionDate.first! {
releases.append(release)
release = [Int]()
completionDate.removeSubrange(0..<(completionDate.firstIndex(of: date)!))
release.append(completionDate[0])
} else {
release.append(date)
}
if release == completionDate {
releases.append(release)
}
}
return releases.map { $0.count }
}
/**
문제 설명
수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.
1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...
1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.
제한 조건
시험은 최대 10,000 문제로 구성되어있습니다.
문제의 정답은 1, 2, 3, 4, 5중 하나입니다.
가장 높은 점수를 받은 사람이 여럿일 경우, return하는 값을 오름차순 정렬해주세요.
입출력 예
answers return
[1,2,3,4,5] [1]
[1,3,2,4,2] [1,2,3]
입출력 예 설명
입출력 예 #1
수포자 1은 모든 문제를 맞혔습니다.
수포자 2는 모든 문제를 틀렸습니다.
수포자 3은 모든 문제를 틀렸습니다.
따라서 가장 문제를 많이 맞힌 사람은 수포자 1입니다.
입출력 예 #2
모든 사람이 2문제씩을 맞췄습니다.
*/
제출한 답안
import Foundation
func solution(_ answers:[Int]) -> [Int] {
let onePattern = [1, 2, 3, 4, 5]
var oneCount = 0
let twoPattern = [2, 1, 2, 3, 2, 4, 2, 5]
var twoCount = 0
let threePattern = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]
var threeCount = 0
for i in 0...(answers.count - 1) {
if answers[i] == onePattern[i % 5] {
oneCount += 1
}
if answers[i] == twoPattern[i % 8] {
twoCount += 1
}
if answers[i] == threePattern[i % 10] {
threeCount += 1
}
}
print(oneCount)
print(twoCount)
print(threeCount)
var rankingDic = [Int: Int]()
rankingDic[1] = oneCount
rankingDic[2] = twoCount
rankingDic[3] = threeCount
let rankingSort = rankingDic.sorted{ $0.key < $1.key }.filter{ $0.value == (rankingDic.values.max()) }.map { $0.key }
print(rankingSort)
return rankingSort
}
풀이
처음에는 패턴을 통해 answers와 같은 크기의 배열을 생성한 뒤에 배열에서 다시 answers와 비교하였는데 배열 생성할 필요가 없다는걸 알게되어 패턴에서 바로 비교하였습니다.
그리고 패턴 비교를 통해 맞힌 개수(oneCount)를 계산했습니다.
여기까지는 쉬웠는데 그 뒤가 어려웠어요.
- 수포자 순서 뽑는 과정
1, 2, 3번 수포자에 각각 Count를 할당하고 그 뒤에 Count 높은 순으로 정렬하고 제일 높은 값과 같이 않으면 삭제한 뒤 수포자 순서대로 재정렬을 해야겠다는 생각을 했습니다.
그러나 Count 높은 순으로 정렬을 한 뒤 다시 수포자 순서대로 정렬을 하려니 도저히 떠오르지 않아 몇시간을 헤매다가 결국 사이트 답을 보았습니다.
답을 보니 sorted, filter, map 을 같이 쓰네요.
정렬을 한 뒤 값이 같다면 다시 재정렬이 아니라, 수포자 순으로 정렬을 시킨 뒤 제일 큰 값과 같은 값만 다시 담아 오는 거였습니다.
sorted 로 수포자 순으로 정렬을 시킨 뒤, 배열에서 가장 높은 값과 같은 값만 남겨주고 이 배열이 딕셔너리로 되어있기 때문에 배열로 반환을 해주기 위해 수포자 키 값만 map에 담아 배열을 만들어줍니다.
sorted로 정렬을 시키고 filter로 큰 값만 가져오는게 핵심이였습니다.
답안 수정
import Foundation
func solution(_ answers:[Int]) -> [Int] {
let patterns = (
a: [1, 2, 3, 4, 5],
b: [2, 1, 2, 3, 2, 4, 2, 5],
c: [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]
)
var ranking = [1: 0, 2: 0, 3: 0]
for (i, v) in answers.enumerated() {
if v == patterns.a[i % 5] {
ranking[1]! += 1
}
if v == patterns.b[i % 8] {
ranking[2]! += 1
}
if v == patterns.c[i % 10] {
ranking[3]! += 1
}
}
let rankingSort = ranking.sorted{ $0.key < $1.key }.filter{ $0.value == (ranking.values.max()) }.map { $0.key }
return rankingSort
}
각 pattern 을 patterns로 묶고 answers.enumerated() 를 통해 인덱스와 값을 활용했습니다.
import UIKit
// 1. 후행 클로저: 함수의 매개변수 마지막으로 전달되는 클로저는 후행클로저(trailing closure)로 함수 밖에 구현 가능
// 2. 반환타입 생략: 컴파일러가 클로저의 타입을 유추할 수 있는 경우 매개변수, 반환 타입을 생략 가능
// 3. 단축 인자 이름: 전달인자의 이름이 굳이 필요없고, 컴파일러가 타입을 유추할 수 있는 경우 축약된 전달인자 이름($0, $1, $2 ...)을 사용 가능
// 4. 암시적 반환 표현: 반환 값이 있는 경우, 암시적으로 클로저의 맨 마지막 줄은 return 키워드를 생략하더라도 반환 값으로 취급
/// 기본 클로저 표현
// 클로저를 매개변수로 갖는 함수 calcaulated(a: b: method:)와 결과값을 저장할 변수 result 선언
func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int {
return method(a, b)
}
var result: Int
/// 1. 후행 클로저
// 클로저가 함수의 마지막 전달인자일때, 마지막 매개변수 이름을 생략한 후 함수 소괄호 외부에 클로저를 구현 가능
result = calculate(a: 10, b: 10) { (left: Int, right: Int) -> Int in
return left + right
}
print(result) // 20
/// 2. 반환타입 생략
// calculate(a:b:method:) 함수의 method 매개변수는 Int 타입을 반환할 것이라는 사실을 컴파일러도 알기 때문에 굳이 클로저에서 반환타입을 명시해 주지 않아도 된다. 대신 in 키워드는 생략 불가
result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) in
return left + right
})
print(result) // 20
/// 3. 단축 인자이름
// 클로저의 매개변수 이름이 굳이 불필요하다면 단축 인자이름을 활용 가능.
// 단축 인자이름은 클로저의 매개변수의 순서대로 $0, $1, $2 ... 처럼 표현
result = calculate(a: 10, b: 10, method: {
return $0 + $1
})
print(result) // 20
// 당연히 후행 클로저와 함께 사용 가능
result = calculate(a: 10, b: 10) {
return $0 + $1
}
print(result) // 20
/// 4. 암시적 반환 표현
// 클로저가 반환하는 값이 있다면 클로저의 마지막 줄의 결과값은 암시적으로 반환값으로 취급
result = calculate(a: 10, b: 10){
$0 + $1
}
print(result) // 20
// 간결하게 한 줄로 표현 가능
result = calculate(a: 10, b: 10) { $0 + $1}
print(result) // 20
/// 축약 전과 후 비교
// 축약 전
result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) -> Int in
return left + right
})
// 축약 후
result = calculate(a: 10, b: 10) { $0 + $1 }
print(result) // 20
// 너무 축약된 코드는 타인이 보거나, 시간이 지난 뒤에 볼 때 명시성이 떨어질 수 있으므로 적정선에서 축약
import UIKit
/// 1. 클로저
// 실행가능한 코드 블럭
// 함수와 다르게 이름정의는 필요하지 않지만, 매개변수 전달과 반환 값이 존재
// 함수는 이름이 있는 클로저
// 일급객체로 전달인자, 변수, 상수 등에 저장 및 전달 가능
/// 2. 기본 클로저 문법
// 클로저는 중괄호 {}로 감싼다
// 괄호를 이용해 파라미터를 정의
// -> 을 이용해 반환 타입을 명시
// "in" 키워드를 이용해 실행 코드와 분리
/*
{(매개변수 목록) -> 반환타입 in
실행 코드
}
*/
/// 3. 클로저 사용
// sum이라는 상수에 클로저를 할당
let sum: (Int, Int) -> Int /* 클로저 타입 표현 */ = {(a: Int, b: Int) in
return a + b
}
let sumResult: Int = sum(1, 2)
print(sumResult) // 3
/// 4. 함수의 전달인자로서의 클로저
// 클로저는 주로 함수의 전달인자로 많이 사용
// 함수 내부에서 원하는 코드블럭을 실행
let add: (Int, Int) -> Int
add = {(a: Int, b: Int) in
return a + b
}
let substract: (Int, Int) -> Int
substract = {(a: Int, b: Int) in
return a - b
}
let divide: (Int, Int) -> Int
divide = {(a: Int, b: Int) in
return a / b
}
func calculate(a: Int, b: Int, method: (Int, Int) -> Int) -> Int {
return method(a, b)
}
var calculated: Int
calculated = calculate(a: 50, b: 10, method: add)
print(calculated) // 60
calculated = calculate(a: 50, b: 10, method: substract)
print(calculated) // 40
calculated = calculate(a: 50, b: 10, method: divide)
print(calculated) // 5
// 따로 클로저를 상수/변수에 넣어 전달하지 않고,
// 함수를 호출할 때 클로저를 작성하여 전달 가능
calculated = calculate(a: 50, b: 10, method: {(left: Int, right: Int) -> Int in
return left * right
})
print(calculated) // 500
또한, REST API는 HTTP를 이용한 것이기 때문에 HTTP Method를 사용한다.
3) 자원의 형태 (JSON)
클라이언트가 HTTP Method를 통해서 데이터 조작을 요청하면, 서버가 적절한 응답을 보내게 된다.
이때 하나의 자원은 JSON, XML, TEXT 등 여러 형태로 표현된다.
보통은 JSON, XML을 통해 데이터를 주고 받는다.
3. REST API와 RESTFul
REST API는 REST 기반으로 서비스 API를 구현한 것을 의미한다.
1) REST API 설계 규칙
REST API를 올바르게 설계하기 위해서는 지켜야하는 몇가지 규칙이 있다.
① URI는 동사보다는 명사를, 대문자보다는 소문자를 사용해야 한다. ② 행위를 포함하지 않는다. ③ 언더바(_) 대신 하이픈(-)을 사용한다. ④ 파일 확장자(.jgp 등)는 URI에 포함하지 않는다. ⑤ 마지막에 슬래시(/)를 포함하지 않는다. → 슬래시 구분자는 계층 관계를 나타낼때 사용하므로 맨 마지막에는 사용하지 않는 것이 좋다.
iOS 앱과 안드로이드 앱을 모두 자바스크립트 환경에서 네이티브로 개발할 수 있는 환경입니다. 크로스 플랫폼 앱이지요.
때문에 node.js, iOS, 안드로이드를 위한 다양한 설정 과정이 필요합니다.
iOS 앱의 개발을 위해서는 OS X가 필수적입니다. 기술적으로 보자면 Android 앱의 개발은 플랫폼과는 별도로 독립적일 수 있지만 iOS 앱의 개발은 OS X 전용으로 개발이 되있습니다. 그래서 React Native로 앱을 개발하기 위해서는 맥 환경을 사용해야 합니다.
특징
첫번째, 자바스크립트 기반
자바스크립트 라이브러리의 한 종류인 리액트와 동일한 문법, 개념을 공유하고 있어요. 그래서 자바스크립트를 다룰 줄 아는 분이라면, 특히 리액트 라이브러리를 다룬 적이 있다면 쉽게 배울 수 있습니다. 페북, 인스타그램 앱의 일부를 리액트 네이티브로 제작하고 있어요.
두번째, 오픈소스
React Native는 오픈 소스로 커뮤니티가 활성화되어 있습니다. 모르는 정보가 있다면 블로그에서 정보를 찾아 볼수 있어서 이런 장점 덕분에 React Native는 크로스 플랫폼 개발의 한 축을 담당하고 있습니다.
/**
문제
서로 다른 N개의 자연수의 합이 S라고 한다. S를 알 때, 자연수 N의 최댓값은 얼마일까?
입력
첫째 줄에 자연수 S(1 ≤ S ≤ 4,294,967,295)가 주어진다.
출력
첫째 줄에 자연수 N의 최댓값을 출력한다.
예제 입력 1
200
예제 출력 1
19
*/
제출한 답안
import Foundation
func maximum(max S: Int) -> Int {
var sum = 0
var N = 0
for i in 1... {
sum += i
guard sum <= S else {
break
}
N = i
}
return N
}
let S = Int(readLine()!)!
print(maximum(max: S))
풀이
처음에 읽어보았을때 N의 최댓값은 결국 제일 작은 숫자인 1부터 하나씩 증가한 값을 더해가는 경우의 최대값이라는 생각이 들었습니다.
1부터 하나씩 더해가려면 for문 1... 을 활용하면 좋을거 같았어요.
특정 조건일때 멈추려면 java라면 if break 가 있겠지만, swift는 특정 조건이 아닐때만 멈추면 되니 guard문이 더 괜찮았습니다.
더하고 나서 멈추려니 200입력에서 20이 출력되는 즉, sum이 200초과한 상태에서 S와 비교가 되어서 어떻게 하면 전 상태의 값을 기억할 수 있을까 생각해보았습니다.
해결법은 'i를 sum에 더한 다음에 i값을 N에 따로 저장하되 비교하기 전에 저장해 놓자' 였습니다. 그래서 guard문을 N 전에 놓았습니다.
Uploaded by Notion2Tistory v1.1.0