개발, 공부, 일상 블로그

[Swift] 초기화 해지 (Deinitialization)

|

초기화 해지

클래스 인스턴스가 소멸되기 직전에 호출, deinit 키워드를 사용
클래스 당 오직 하나의 디이셜라이저만 선언 가능, 파라미터를 받을 수 없음

class Player {
	var name: String
	init(_ name: String) {
		self.name = name
		print("플레이어 \(name) 생성됨")
	}
	deinit() {
		print("플레이어 \(name) 소멸됨")	
	}
}

var player = Player("test") // 플레이어 test 생성됨
player = nil // 플레이어 test 소멸됨

[Swift] 초기화 (Initialization)

|

초기화

프로퍼티의 초기 값 설정 단계, 이니셜라이저를 정의

스위프트의 이니셜라이저는 다른 언어의 생성자와 비슷한 역할

이니셜라이저

init 키워드 사용

class SomeClass {
	var someProperty: Int
	var propertyWithDefaultValue = 1 // 선언과 동시에 초기화
	
	// 파라미터가 없는 이니셜라이저
	init() {
		someProperty = 1
	} // let someClass = SomeClass()
	
	// 파라미터가 있는 이니셜라이저
	init(someProperty: Int) {
		self.someProperty = someProperty
	} // let someClass = SomeClass(someProperty: 10)
	
	// 같은 타입의 파라미터로 오버로딩 (이름으로 구분)
	init(propertyWithDefaultValue: Int) {
		self.propertyWithDefaultValue = propertyWithDefaultValue
	} // let someClass = SomeClass(propertyWithDefaultValue: 10)
	
	// 인자 레이블이 없는 이니셜라이저 파라미터
	init(_ value: Int) {
		self.someProperty = value
	} // let someClass = SomeClass(10)
}

편리한 초기자

convenience 키워드 사용, 일반적인 init 초기자 (지정 초기자) 보다 먼저 호출됨

class Food {
	var name: String
	init(name: String) {
		self.name = name
	}
	convenience init() {
		self.init(name: "default")
	}
}

슈퍼클래스에서의 편리한 초기자 오버라이딩

class Meat: Food {
	var quantity: Int
	init(name: String, quantity: Int) {
		self.quantity = quantity
		super.name = name
	}
	override convenience init(name: String) {
		self.init(name: name, quantity: 1)
	}
}

이렇게 하면 3가지의 형태로 인스턴스 생성 가능

1. let meat = Meat() // name: default, quantity: 1
2. let meat = Meat(name: "소고기") // name: 소고기, quantity: 1
3. let meat = Meat(name: "소고기", quantity: 3) // name: 소고기, quantity: 3

실패 가능한 초기자

init? 키워드 사용, nil을 반환하여 인스턴스의 값이 nil이 됨

struct Animal {
	let species: String
	init?(species: String) {
		if species.isEmpty { return nil }
		self.species = species
	}
}

필수 초기자

모든 서브클래스에서 반드시 구현해야 하는 초기자, required 키워드를 붙여줌

class SomeClass {
	required init() {
		...
	}
}

class SomeSubclass: SomeClass {
	required init() {
		...
	}
}

클로저를 이용한 기본 프로퍼티 값 설정

struct Gugudan {
	let values: [Int] = {
		var tempValues = [Int]()
		for i in 1..9 {
			for j in 1..9 {
				tempValues.append(i * j)
			}
		}
		return tempValues
	}()
	
	subscript(i: Int, j: Int) {
		return values[(i-1)*9 + (j-1)]
	}
}

[Swift] 상속 (Inheritance)

|

상속

기반클래스: 아무것도 상속받지 않은 클래스

서브클래싱

class SomeSuperclass {
	var a = 0
}

class SomeSubclass: SomeSuperclass {
	var b = 1
}

let someSubclass = SomeSubclass()
someSubclass.a // SomeSuperclass의 프로퍼티
someSubclass.b // SomeSubclass의 프로퍼티

오버라이딩

메소드 오버라이드

메소드 선언 앞에 override 키워드를 붙임

class Vehicle {
	func makeNoise() {
		print("빵빵")
	}
}

class Bicycle: Vehicle  {
	override func makeNoise() {
		print("따르릉")
	}
}

let bicycle = Bicycle()
bicycle.makeNoise() // 따르릉

프로퍼티 오버라이드

class Vehicle {
	var speed = 0.0
	var description: String {
		return "속도: \(speed) km/h"
	}
}

class Bicycle: Vehicle  {
	var gear = 1
	override var description: String {
		return "\(super.description), 기어: \(gear)"
	}
}

let bicycle = Bicycle()
bicycle.speed = 30.0
bicycle.gear = 3
bicycle.description // 속도: 30.0 km/h, 기어: 3

오버라이드 방지

final 키워드로 선언

class Vehicle {
	final func makeNoise() {
		print("빵빵")
	}
}

class Bicycle: Vehicle  {
	override func makeNoise() { // 컴파일 시 에러 발생
		print("따르릉")
	}
}

[Swift] 서브스크립트 (Subscript)

|

서브스크립트

콜렉션, 리스트, 시퀀스 등 집합의 특정 멤버 엘리먼트에 간단하게 접근할 수 있는 문법

문법

// 기본 사용법
subscript(index:  Int)  ->  Int  {
	get  {
		return ~ // 적절한 반환 값
	}
	set(newValue)  {
	// 적절한 set 액션
	}
}

// 읽기 전용 (get은 생략)
subscript(index:  Int)  ->  Int  {
	return ~ // 적절한 반환 값
}

// 호출
someInstance[index] = newValue // 서브스크립트의 set 호출
print(someInstance[index]) // 서브스크립트의 get 호출

[Java] 트위치 후원 알림 API (Twip, Toonation API)

|

💸 Donation Alert API

오늘은 내가 만든 트위치 후원 알림 API를 소개하려고 한다.
정확히 말하자면, TwipToonation의 Alertbox 알림을 받아올 수 있는 API이다.

😎 왜 만들게 되었나!

오래 알고지내는 한 마인크래프트 유튜버가 혹시 트윕 후원 내용을 마인크래프트로 보여줄 수 없냐고 물어봐서 만들게 되었다.

그리고 이렇게 쓰였다.
참고로 이 영상의 0:45초에 나오는 염료가 마인크래프트 평행세계의 나다.

그래서.. 사실 만든지는 1년정도 됐다.

그러다가 며칠 전 Twip의 1.1.60 패치 이후에 플러그인을 손 볼 일이 생겼다.
오랜만에 보니까 참 감회가 새로웠는데, 이 도네이션 파싱(?) 기능만 따로 API로 만들어두면 누군가 유용하게 쓸 수 있지 않을까? 라는 생각이 들었다.

그래서 이것저것 떼어내고, 쓰기 쉽게 바꾸고… 하다보니 완성했다!

다 완성하고 나니 다른 라이브러리들처럼 깔끔하게 dependencies에 한 줄만 추가해서 사용할 수 있으면 얼마나 좋을까? 라는 생각을 하게되었고

운 좋게도 나는 예전에 Jitpack이라는 것을 사용해봤으므로 Jitpack을 사용해서 아름답게 배포했다. (그냥 repository만 추가하면 된다.)

🤔 어떻게 만들었나?

먼저 크롬의 개발자 도구로 Alertbox 위젯이 어떤 주소로 웹소켓에 연결하는지, 어떤 이벤트를 수신하는지 등을 살펴봤다.

트윕은 socket.io, 투네이션은 WebSocket(okhttp)을 사용해서 서로 다른 두 가지를 모두 써볼 수 있었다.
(물론 클라이언트만, 그리고 okhttp는 socket.io에 내장되어있다.)

그리고 RxJava라는 멋쟁이들에게만 허용된 라이브러리를 사용했다.
웹소켓에 연결하고, 후원 발생 시 구독한 Observer들에게 이벤트를 전달한다!

웹소켓을 통해 주고받는 메시지는 당연하게도 JSON 형식이라, json-simple 라이브러리를 사용했다.
(마인크래프트의 bukkit이 json-simple을 내장하고 있기 때문에, 처음 만들때부터 json-simple을 사용하고 있었다.)

우선은 내가 필요한 기능들만 구현해 놨는데, 누군가가 필요로 한다면 더 나아가서 Twip과 Toonation의 전체 기능에 대한 API로 확장 시켜보는 것도 재밌을 것 같다.


✨ 소개합니다. Donation Alert API

Twip, Toonation의 후원 알림(Alertbox)을 받아올 수 있는 RxJava 기반 Java API

so much money

outstandingboy/DonationAlertAPI


🚀 Start

Gradle (use Jitpack)

repositories {
    ...
    maven { url 'https://jitpack.io' }
}

dependencies {
    ...
    compile 'com.github.outstanding1301:donation-alert-api:1.0.0'
}

Twip

// Twip Alertbox URL의 마지막 https://twip.kr/widgets/alertbox/<YOUR_TWIP_KEY> 부분을 입력하세요.
Twip twip = new Twip("YOUR_TWIP_KEY");

// 메시지를 구독합니다.
// 연결 알림, 에러 등의 String 메시지를 처리하는 핸들러를 인자로 사용합니다. 
twip.subscribeMessage(s -> System.out.println(s));

// 도네이션 알림을 구독합니다.
// Donation 객체를 처리하는 핸들러를 인자로 사용합니다.
twip.subscribeDonation(donation -> {
    System.out.println("[Twip] "+donation.getNickName()+"님이 "+donation.getAmount()+"원을 후원했습니다.");
    System.out.println("후원 내용: "+donation.getComment());
});

Toonation

// Toonation Alertbox URL의 마지막 https://toon.at/widget/alertbox/<YOUR_TOONATION_KEY> 부분을 입력하세요.
Toonation toonation = new Toonation("YOUR_TOONATION_KEY");

// 메시지를 구독합니다.
// 연결 알림, 에러 등의 String 메시지를 처리하는 핸들러를 인자로 사용합니다. 
toonation.subscribeMessage(s -> System.out.println(s));

// 도네이션 알림을 구독합니다.
// Donation 객체를 처리하는 핸들러를 인자로 사용합니다.
toonation.subscribeDonation(donation -> {
    System.out.println("[Toonation] "+donation.getNickName()+"님이 "+donation.getAmount()+"원을 후원했습니다.");
    System.out.println("후원 내용: "+donation.getComment());
});

📃 Docs

Donation

식별자 타입 설명
id String 후원자 ID
nickname String 후원자 닉네임
comment String 후원 내용
amount Integer 후원 금액


Platform (Twip, Toonation)

식별자 타입 설명
subscribeDonation(Consumer onNext) void 후원 알림 구독
subscribeMessage(Consumer onNext) void API 메시지 구독
close() void 연결 종료
getDonationObservable() Subject 후원 알림 Subject 객체 반환
getMessageObservable() Subject API 메시지 Subject 객체 반환

💉 Dependencies

implementation 'io.socket:socket.io-client:1.0.0'
implementation  'io.reactivex.rxjava2:rxjava:2.1.16'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.googlecode.json-simple:json-simple:1.1.1'