iOS 스터디 자료로 만든 문법 정리
import Foundation
// var a: Int
// a = nil // a에는 nil을 넣을 수 없음. 무조건 초기화를 시켜줘야함
let stock: Int? = nil // 값이 없음.(NULL과 비슷함) 타입을 항상 명시적으로 표시해야함
print(stock)
let str: String? = nil
let optionalStr: String? // ?만 붙여도 옵셔널을 선언할 수 있음(nil)
let a:Int? = nil
let b = a
b
let c: Optional<Int> = nil
var num: Int?
print(num)
num = 123
print(num) // wrapping 되어 있어서 값을 넣어도 전달되지 않음
print(num!) // 강제로 값을 추출. (Unwrapping)
let n = 123
print(n)
num = 123
let before = num
let after = num!
import Foundation
// 옵셔널의 값이 존재하는지를 검사한 뒤, 존재한다면 그 값을 다른 변수에 대입 (추출)
var num: Int? = 123
if let n = num { // n은 옵셔널 값이 아님. num의 값이 존재 하므로 n의 num값 대입. 대입하는 편이 오류가 적고 안정적임.
print(n) // (명시적해제 - 비강제해제
}
if num != nil { // 옵셔널 값 추출. 옵셔널의 값이 존재하면 강제로 추출 (명시적해제 - 강제해제)
print(num!)
}
var str: String? = "Swift"
guard let str else {
print(str!)
fatalError() // gaurd 리턴문
}
let a: Int? = 12
let b: String? = "str"
if let a, let b, b.count < 5 { // b의 글자수가 5보다 작으면 a,b 출력
print(a,b)
}
if let a, b!.count < 5{ // a는 새로 선언되어 옵셔널 값이 아니지만 b는 옵셔널이기 때문에 추출 할 때 !를 붙여서 강제로 추출해서 비교.
print(a,b) // b를 새로운 상수로 선언하면 b의 길이를 비교하는 구문에서의 b에서 !를 안붙여도 됨
}
if let b, a! > 3, b.count < 5 { // 비교 구문이 아니면 무조건 새로 상수 선언
print(a,b) //새로 선언하지 않고 비교연산에서 강제 추출 할 경우 업래핑된 상태라 출력값이 래핑된 상태로 출력이 됨.
}
if let aNum = a , let bNum = b , bNum.count < 5 { // 값을 대입해서 하는것이 가장 안정적임
print(aNum,bNum)
}
if let aNum = a , let bNum = b , b!.count < 5 { // 비교연산에 한하여 b를 강제 추출 할 수 있음
print(aNum,bNum)
}
print(a!, b!) // 이렇게 바로 강제 추출도 가능함.
if let a, let b { //새로 선언해주면 강제 추출 하지 않아도 사용가능
print(a,b)
}
if a == 12 { // 비교연산자를 통한 옵셔널값의 묵시적해제. 하지만 출력이나 연산에서는 해제가 되지 않음
print("a = 12")
print(a!+1) // 이렇게 !를 붙여야 연산가능
}
let a1: Int! = 12
print(a1+1) // 연산할때나 비교할때 묵시적 해제가 되어 사용가능
import Foundation
struct SPerson { // 구조체
var name: String // 값을 저장하지 않아도 됨.
var age: Int
func speak(){
print("Hello")
}
}
let s = SPerson(name: "Steve", age: 50)
let name = "Pual" // 오류가 나지 않음. 접근방식이 다름. 구조체의 name이 아님.
s.name
s.age
s.speak()
class CPerson { // 값을 먼저 저장해 주어야함
var name: String = "Steve"
var age: Int = 50
func speak() {
print("Hello")
}
}
let c = CPerson()
c.name
c.age
import Foundation
struct PositionValue { // 값형식
var x = 0.0
var y = 0.0
}
class PositionObject { // 참조형식
var x = 0.0
var y = 0.0
}
var v = PositionValue()
v.x
v.y
var o = PositionObject()
o.x
o.y
var v2 = v
v2.x
v2.y
var o2 = o
o2.x
o2.y
v2.x = 12 // v2는 바뀌지만 v는 바뀌지 않음. (v2는 복사본)
v2.y = 34
v.x
v.y
v2.x
v2.y
o2.x = 12 // o2도 바뀌고 o도 바꿈. 새로운 복사본을 만들지 않고 원본을 그대로 전달함. 참조를 전달함(o의 메모리 주소 전달)
o2.y = 34
o.x
o.y
o2.x
o2.y
import Foundation
struct Person {
let name: String = "Chan Sol"
var age: Int = 26
}
var p = Person()
p.name
p.age
p.age = 30 // 구조체의 가변성은 속성의 가변성에 영향을 줌
struct Image {
init() {
print("new image")
}
}
struct BlogPost {
let title: String = "Title"
let content: String = "Content"
var notLazy: Image = Image() // 지연저장하지 않아서 print 실행됨
lazy var attachment: Image = Image() // 지연저장. 이 속성은 초기화가 되지 않음. 초기화자에서 실행 및 초기화가 되지않음.
// 지연 저장 되었기때문에 print가 실행되지않음
let date: Date = Date()
// 위 Date속성이 초기화되어서 formattedDate속성은 초기화 될수 없음. 따라서 lazy를 사용해서 지연 시켜야함
lazy var formattedDate: String = { // lazy는 무조건 var로 선언
let f = DateFormatter()
f.dateStyle = .long
f.timeStyle = .medium
return f.string(from: date)
}()
}
var post = BlogPost() // 구조체에서 지연저장 속성을 사용하려면, 반드시 변수에 저장해야함.
post.attachment // 이렇게 접근해야지 초기화됨(print실행)
import Foundation
class Person {
var name: String
var yearOfBirth: Int
init(name: String, year: Int) {
self.name = name
self.yearOfBirth = year
}
var age: Int { // 현재 나이를 계산
get { // age를 읽을 때 실행
let calendar = Calendar.current
let now = Date()
let year = calendar.component(.year, from: now)
return year - yearOfBirth // 현재 연도에서 태어난 연도를 뺌
}
set { // age에 새 값을 넣을 때 실행
let calendar = Calendar.current
let now = Date() // 현재 날짜 및 시간
let year = calendar.component(.year, from: now) // 현재의 년도를 호출하는 메소드
yearOfBirth = year - newValue // 현재 연도에서 나이를 뺌
}
}
}
let p = Person(name: "James", year: 2002) // get 블록이 실행.
p.age
p.yearOfBirth
// 이후에 age 값을 변경하면 set 블록이 실행되서 클래스 및 속성의 value 모두 변경
p.age = 50 // 현재 연도에서 age즉 현재 나이를 뺀값이 yearOfBirth에 들어감. set 블록 실행. (newVlaue 값을 넣어줌)
p.yearOfBirth
import Foundation
struct/*class*/ Size {
var width = 0.0
var height = 0.0
mutating func reset(value: Double) {
// width = value
// height = value
self = Size(width: value, height: value) // 위 코드와 같이 사용 할 수 있음 (class에서 사용불가. 값형식에서만 사용가능)
}
// func calcArea() -> Double {
// return width * height // self 생략
// }
//
// var area: Double{
// return calcArea() // self 생략
// }
//
// func update(width: Double, height: Double) {
// self.width = width // 파라미터 이름이 같아서 self 생략 불가능
// self.height = height
// }
//
// func doSonthing() {
// let c = { self.width * self.height } // 클로저 안에 있으므로 self 생락 불가능
// }
//
// static let unit = ""
//
// static func doSomthing() {
// //self.height 형식 메소드에서 인스턴트 메소드를 직접 접근하는거는 불가능함.
// unit // self 생략가능. 타입 멤버(static)
// }
}
import Foundation
// 함수가 클로저를 리턴함
func makeCounter() -> () -> Int {
var count = 0 // 이 변수는 클로저 안에서 캡처(capture)됨
// 이 아래 클로저는 () -> Int 타입
// 외부 함수가 끝나도 클로저는 count 값을 기억하고 있음
let counter = {
count += 1 // 외부의 count를 계속 증가시킴
return count
}
return counter // 함수는 클로저를 리턴하고 종료
}
// makeCounter()를 호출하면 클로저를 하나 만들어서 리턴함
let counter1 = makeCounter() // 여기서 count = 0이 캡처됨
print(counter1()) // 1 → 클로저 내부 count가 1 됨
print(counter1()) // 2 → 클로저 내부 count가 2 됨
print(counter1()) // 3 → 계속 유지됨 (클로저가 count를 기억하고 있기 때문)
// 새로운 클로저를 만들면 count는 새로 생김!
let counter2 = makeCounter() // 또 다른 클로저, 또 다른 count = 0
print(counter2()) // 1 → 새롭게 시작됨
import Foundation
class Sample {
var data = 0 // 인스턴스 멤버
nonisolated(unsafe) static var sharedData = 123 // 타입멤버
func doSomething() {
print(data) // 같은 인스턴스(클래스 안에 있음) 그냥 사용 가능
Sample.sharedData // 인스턴스 멤버에서 타입멤버에 접근 할 때는 타입이름으로 접근해야함
}
func call() {
doSomething() // 같은 인스턴스 멤버이기 때문에 self를 생략가능 (self.doSomething)
}
}
let a = Sample()
a.data
a.doSomething() // 반드시 인스턴스 이름을 통해서 호출 해야함
Sample.sharedData = 10
a.call()
struct Size {
var width = 0.0
var height = 0.0
mutating func enlarge() { // 가평식에서 속성을 바꾸는 메소드를 구현 할 때는 mutating으로 선언해야함(중요)
width += 1.0
height += 1.0
}
}
var s = Size() // mutating 메소드를 호출 할때는 인스턴스 메소드를 변수로 선언해야함
s.enlarge()
import Foundation
class Position {
var x: Double
var y: Double
// var z = 0.0 // 잘 사용하지 않음
init() { // 파라미터가 없는 초기화자
x = 0.0
y = 0.0
}
init(value: Double) { // 파라미터가 있는 초기화자
x = value
y = value
}
}
let a = Position() // 파라미터가 없는 초기화자 호출
a.x
a.y
let b = Position(value: 100) // 파라미터가 있는 초기화자 호출
b.x
b.y
class SizeObj {
var width = 0.0
var height = 0.0
init(width: Double, height: Double) {
self.width = width // self 사용
self.height = height
}
convenience init(value: Double) { // 간편초기화자
// width = value
// height = value
self.init(width: value, height: value)
}
}
struct SizeValue {
var width = 0.0
var height = 0.0
}
let s = SizeValue()
SizeValue(width: 0.0, height: 0.0) // 구조체의 특별한 Initializer
import Foundation
class Position {
var x: Double
var y: Double
init(x: Double, y: Double) { // Degignated Initializer. 모든 속성을 초기화. 필수
self.x = x
self.y = y
}
convenience init(x: Double) { // Convenience Initializer. 초기화 하고 싶은 속성만 초기화. 필수X
self.init(x: x, y: 0.0)
}
}
class Figure {
var name: String
/*required*/ init(name: String) {
self.name = name
}
func draw() {
print("draw \(name)")
}
convenience init() { // name을 안받아오면 name을 "Unknown" 으로 초기화해주는 초기화자
self.init(name: "Unknown")
}
}
class Rectangle: Figure { // 상속된 초기화자가 일부 속성만 초기화해서 오류.
var width: Double // = 12
var height: Double// = 34 // 이렇게 초기화자에서 초기화되지 않은 속성들도 초기화 해줘야함.
init(name: String, width: Double, height: Double) {
self.width = width
self.height = height
super.init(name: name) // 수퍼 클래스에 있는 초기화자에서 초기화하는 속성이 있으면 수퍼 클래스의 초기화자로 넘겨야함.
}
override init(name: String) {
self.width = 0
self.height = 0
super.init(name: name)
}
}
class Rectangle2: Figure { // Required Initializer
var width: Double = 12
var height: Double = 34
init() {
width = 0.0
height = 0.0
super.init(name: "unknown")
}
// required init(name: String) { 수퍼클래스의 초기화자와 똑같은 형태로 구현. 수퍼클래스 초기화자초기화자 선언할 때 required를 앞에 붙임
// width = 0.0
// height = 0.0
// super.init(name: "name")
// } // 서브 클래스를 상속한 다른 클래스들이 다시 이 메소드(상속자)를 구현하도록 강제함.
}
import Foundation
class Figure {
var name = "Unknown"
init(name: String) {
self.name = name
}
func draw() {
print("draw \(name)")
}
}
class Circle: Figure {
var radius = 0.0
}
let c = Circle(name: "Circle") // 상속받은 sub 클래스 이기 때문에 super 클래스의 name 사용가능
c.radius
c.name
c.draw()
let a = Figure(name: "Figure")
a.name // super 클래스의 값은 변함
c.name // super클래스에서 상속받은 super클래스의 값은 변하지 않음
c.draw()
class Rectangle: Figure { // Rectangle 클래스는 Figure 클래스를 상속
var width = 0.0
var height = 0.0
}
final class Impossible: Figure { // Figure 클래스를 상속 받았지만 다른 클래스를 상속하는것은 불가능 (Final)
}
class Square: Rectangle { // Square 클래스는 Figure 클래스를 상속받은 Rectangle 클래스를 상속
}
import Foundation
class Figure {
var name = "Unknown"
init(name: String) {
self.name = name
}
/*final*/ func draw() { // 멤버를 final로 선언하면 오버라이딩이 금지되는거고 상속대상이 제외되는것은 아님
print("draw \(name)")
}
}
class Circle: Figure {
var radius = 0.0 // 읽기 쓰기가 가능한 속성을 읽기 전용으로 오버라이드는 못함
var diameter: Double { // 읽기 전용 속성
return radius * 2
}
override func draw() { // override 키워드를 추가해서 super클래스의 멤버를 재정의
super.draw() // super 클래스의 메소드를 실행
print("🔴")
super.draw()
}
}
let c = Circle(name: "Circle")
c.draw() // 오버라이딩한 메소드를 호출. 상위 구현을 완전히 무시
final class Oval: Circle { // 읽기 쓰기가 가능한 속성을 읽기 전용으로 오버라이드
// override var radius = 0.0 속성은 이런 방식으로 오버라이딩 하면안됨. 계산속성이나 프로퍼티 옵저버를 추가하는 방식으로 오버라이드 해야함.
// 읽기, 쓰기 -> 읽기(x), 읽기, 쓰기 -> 읽기, 쓰기(o)
// 읽기 -> 읽기(x), 읽기 -> 읽기, 쓰기(o)
override var radius: Double { // 계산속성. 읽기 쓰기가 가능한 속성은 읽기와 쓰기가 모두 가능해야함.(읽기전용으로 오버라이드 불가능)
get{
return super.radius // 읽기
}
set {
super.radius = newValue // 쓰기
}
}
//읽기 전용으로 선언한 속성을 읽기와 쓰기와 가능한 속성으로 오버라이딩 (계산속성으로 오버라이드 할 때만 가능
override var diameter: Double {
get {
return super.diameter // 읽기. super을 self 바꾸면 안됨. 동일한 속성이 반복적으로 호출됨.
}
set {
super.radius = newValue // 쓰기.
}
}
}
// 프로퍼티 옵저버 오버라이드. 저장속성에만 사용가능(값이 바뀌면 실행)
class Oval2: Circle {
override var radius: Double {
willSet { // 속성의 값이 변경되기 직전 실행
print(newValue)
}
didSet { // 속성의 값이 변경된 직후 실행
print(oldValue)
}
}
// override var diameter: Double { 읽기 전용 계산속성은 감시 할 수 없음. 값이 바뀌지 않는데 값이 바뀌는것을 감시할 수 없음.
// willSet {
// print(newValue)
// }
// didSet {
// print(oldValue)
// }
// }
}
let o = Oval(name: "Oval")
o.radius
o.radius = 12
o.draw()
import Foundation
protocol Something {
func doSomething()
}
struct Size: Something { // 프로토콜을 선언하면 프로토콜을 채용한다고 명시적으로 표시.
func doSomething() { // 프로토콜에 있는 메소드를 구현해야함.
}
}
protocol SomethingObject: AnyObject, Something { // Something 프로토콜을 채용하는 SomethiingObject 프로토콜
}
//struct Value: SomethingObject { // 클래스 전용 프로토콜이라 채용불가(Anyobject)
//
//}
class Object: SomethingObject { // Object 클래스가 SomethingObject 프로토콜을 채용하고, 요구사항을 충족
// Something 프로토콜을 채용하는 SomethiingObject 프로토콜을 채용했으므로, Something 프로토콜의 메소드를 작성해야함.
func doSomething() {
}
}
import Foundation
protocol Resettable {
/*mutating 선언시*/ func reset() // 바디는 작성하지 않아도됨
}
/*struct*/ class Size: Resettable {
var width = 0.0
var height = 0.0
//class면 mutating 안붙여도됨. 왜? class는 참조
//struct면 mutating 붙여야함. 왜? 구조체는 값
func reset() {
width = 0.0
height = 0.0
}
}
// static을 선언하면 메소드 앞에 static선언. 하지만 overriding 불가.
// 메소드앞에 static 대신 class로 선언하면 overriding 가능.
import Foundation
protocol Figure {
static var name: String {get set} //이 프로토콜을 따르는 타입은 name을 읽기/쓰기 가능하게 만들어야 함
}
struct Rectangle: Figure {
nonisolated(unsafe) static /*let //읽기전용만 있으면 let사용 가능*/ var name = "Rect" // var는 읽기 쓰기 모두 가능
}
struct Triangle: Figure {
nonisolated(unsafe) static var name = "Triangle" // 읽기 쓰기 모두 있어서 변수로 선언해야함
}
class Circle: Figure {
static var name: String {
get{
return "Circle"
}
set { // 쓰기도 추가해야함
}
}
}
import Foundation
struct Size {
var width = 0.0
var height = 0.0
}
extension Size { // extention으로 구조체 속성에 area 추가.(기존 타입(struct)에 기능을 추가)
var area: Double {
return width * height
}
}
extension Size: Equatable { // 프로토콜 구조를 추가 할 수 있음. Size연산자를 비교연산자로 비교할 수 있음.
public static func == (lhs: Size, rhs: Size) -> Bool {
return lhs.width == rhs.width && lhs.height == rhs.height
}
}
let a = Size(width: 100, height: 100)
let b = Size(width: 100, height: 100)
if a == b {
print("같음")
}
let s = Size()
s.width
s.height
s.area
extension String { // 이런식으로 기본적인 속성에 대해서도 기능을 추가할 수 있음
var isNotEmpty: Bool {
return !self.isEmpty
}
}
let str = "hello"
if str.isNotEmpty {
print("not empty")
}
import Foundation
let num = 123
num is Int // 타입 체크는 런타임에 실행 (true)
num is Double //(false)
num is String // (false)
class Figure {
let name: String
init(name: String) {
self.name = name
}
func draw() {
print("draw \(name)")
}
}
class Triangle: Figure {
}
class Rectangle: Figure {
var width = 0.0
var height = 0.0
override func draw() {
super.draw()
print("⬛️ \(width) x \(height)")
}
}
class Circle: Figure {
var radius = 0.0
}
class Square: Rectangle {
}
let t = Triangle(name: "triangle")
let r = Rectangle(name: "Rectangle")
let s = Square(name: "Square")
let f = Figure(name: "Figure")
let c = Circle(name: "Circle")
r is Figure // Rectangle은 Figure을 상속받았기 때문에 true
s is Figure // Square은 Figure을 상속받은 Rectangle을 상속받았기 때문에 true
r is Square // false
if let square = r as? Figure {
print("Figure")
} else {
print("Not Figure")
}
var upcasted: Figure = s
upcasted = s as Figure // 이렇게 as를 사용해서 업캐스팅 할 수 있음
// 업캐스팅은 항상 안전하기 때문에 옵셔널바인딩을 하지 않아도 됨
upcasted as? Square
upcasted as! Square
upcasted as? Rectangle
upcasted as! Rectangle
let list = [r, s, t, c] // 리스트의 타입은 가장 슈퍼타입인 Figure
for item in list {
item.draw() // 다형성. 업캐스팅한 인스턴스를 통해서 메소드를 호출하더라도 실제 타입에서 오버라이딩한 메소드가 호출
if let c = item as? Circle { // Circle로 다운캐스팅 한 뒤에 성공한 경우에만 실행 및 접근
c.radius // radius 속성이 선언된 인스턴스를 접근해야만 radius 속성에 접근가능함.
}
}