GnuSmalltalkUsersGuide:BasicChapter 02

From 흡혈양파의 번역工房
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
제 2 장. GNU Smalltalk의 기능

GNU Smalltalk의 기능

이번 장에서는 GNU Smalltalk 특유의 기능을 설명한다. 이러한 기능에는 스몰토크 내부에서 C 함수의 호출, 환경 변수로의 접근, 컴파일과 실행 모니터링의 다양한 측면 제어 기능에 대한 지원을 포함한다.


일반적으로 GNU Smalltalk는 오늘날 스몰토크 구현에 일반적이면서 스몰토크의 ANSI 표준에도 나타나지만 Blue Book에서는 제외된 메서드를 많이 포함하기 때문에 오리지널 Smalltalk-80보다 훨씬 더 강력하다는 사실을 주목하라. Collection의 메서드 allSatisfy: 와 anySatisfy:, 그리고 SystemDictionary (스몰토크 dictionary의 클래스) 내 다수의 메서드가 그 예가 된다.


확장된 스트림

GNU Smalltalk 내 기본 이미지는 ANSI Smalltalk(그리고 Smalltalk-80)에서 발견되는 Stream 계층구조로의 강력한 확장기능을 포함한다. 그 중에서 몇 가지를 꼽으면 다음과 같다:

  • Read stream은 컬렉션에 이용 가능한 모든 반복(iteration) 프로토콜을 지원한다. 일부 경우에서는 (예: fold:, detect:, inject:into:) 완전히 동일하다. select: 또는 collect: 와 같이 새 스트림을 리턴하는 메시지의 경우 블록이 느리게 계산되는데, next를 이용해 스트림으로부터 요소들이 요청되기 때문이다.
  • Read stream은 가령 SequenceableCollections와 같이 , 를 이용해 문자를 결합할 수 있다.
  • 생성기(generator)를 지원하여 가장 빠르게 Stream을 생성할 수 있도록 한다. 생성기는 플러그 가능한 스트림의 일종인데, 사용자가 제공한 블록이 스트림 내 값을 정의하기 때문이다. 예를 들어, 빈 생성기(empty generator)와 두 개의 무한 생성기(infinite generator)가 있다:
    "Returns an empty stream"
    Generator on: [ :gen | ]
    "Return an infinite stream of 1’s"
    Generator on: [ :gen | [ gen yield: 1 ] repeat ]
    "Return an infinite stream of integers counting up from 1"
    Generator inject: 1 into: [ :value | value + 1 ]
    


블록은 "보류"되어 #next 또는 #atEnd 가 생성기로 전송되는 즉시 실행을 시작한다. 블록이 생성기로 #yield: 를 전송하면 다시 블록은 보류되고, 인자가 스트림에서 다음 대상(object)이 된다.


생성기는 연속적(continuations)이지만 스트림과 동일하게 단순한 인터페이스를 제시함으로써 복잡성으로부터 사용자를 보호한다.


정규 표현식 매칭

정규 표현식 또는 "regexes" 라 불리는 것은 텍스트 패턴을 효율적으로 매칭하는 정교한 방식이다. 일반적으로 정규 표현식에 익숙하지 않은 독자는 정규 표현식의 사용 경험이 전혀 없는 이를 위한 가이드 GNU Emacs Manual에서 "Section 20.5 Syntax of Regular Expressions(정규 표현식의 구문)"을 참조한다.


GNU Smalltalk는 String 에서 메서드를 이용해 코어 이미지에서 정규 표현식을 지원한다. GNU Smalltalk 표현식 라이브러리는 GUI libc에서 파생되는데, Perl과 같은 구문의 지원을 위해 Ruby에 이루어진 수정 사항도 함께 전달된다. 이는 항상 포함된 라이브러리를 사용하며, 사용자의 시스템에 설치된 라이브러리는 절대 사용하지 않는다; 향후에는 이전 사양과 호환되는 방식으로 변경될지도 모르겠다. 정규 표현식은 현재 8-bit clean으로, 어떤 일반적인 String과도 작동하지만, 패키지 I18N가 로딩되더라도 전체 유니코드를 지원하지는 않는다는 의미다.


대체로 이러한 정규 표현식은 Perl 5 구문을 지원한다; 레지스터 그룹 '( )'과 반복 '{ }'은 백슬래시와 함께 주어져선 안 되며, 그에 상응하는 리터럴 문자는 백슬래시와 함께 주어져야 한다. 가령 '\{{1,3}'는 '{', '{{', '{{{'와 일치한다; 따라서 '(a)(\( )'는 'a(' 와 일치하고, 'a'와 '('는 각각 첫 번째 레지스터 그룹과 두 번째 레지스터 그룹이다. GNU Smalltalk는 또한 Perl에서와 같이 정규 표현식 변경자 'imsx'를 지원한다. 'im'과 같은 정규 표현식 수정자를 명시하기 위해 스몰토크 문자열 다음에 위치시킬 수는 없는데, 스몰토크 구문에 해당하지 않기 때문이다. 대신 인라인(inline) 변경자 구문을 사용하라. 가령 '(?is:abc.)'는 '[Aa][Bb][Cc](?:.|\n)'과 동일하다.


대부분의 경우 당신은 정규 표현식을 일반 문자열로 명시해야 한다. GNU Smalltalk는 컴파일된 정규 표현식을 항시 캐시 저장하고, 리터럴 문자열을 (예: 대부분의 정규 표현식) 검색 시 특수 고효율 캐싱을 이용해 컴파일된 Regex 객체들을 대부분 코드에서 보지 못하도록 숨긴다. 이러한 캐싱만으로 충분하지 않은 특수 사례의 경우 문자열로 #asRegex 를 전송하면 컴파일된 형태를 가져올 수 있는데, public API 내에서 정규 표현식 문자열을 명시할 장소마다 해당한다. Regex 객체를 이용 시 코드에서 성능의 차이가 눈에 띌 때까지는 항시 캐시에 의존해야 한다.


스몰토크 문자열은 에 의해 주어진 라는 하나의 escape만 갖고 있으므로 정규 표현식 문자열에 사용되는 백슬래시는 백슬래시로서 이해되고, 리터럴 백슬래시는 '\\'를 이용해 즉시 주어질 수 있다.[1]


컴파일된 Regex 객체 상의 메서드는 이 인터페이스에 대해 private하다. Public 인터페이스에 해당하는 GNU Smalltalk는 'regex'라는 범주에서 String 상에 메서드를 제공한다. 매칭, 교체, 패턴 확장, 매치 반복하기를 비롯해 유용한 기능의 메서드가 여러 가지 있다.


기본 연산자는 #searchRegex: 로서 주로 #=~로 적는데, 이러한 표기법은 Perl 구문을 연상시킨다. 해당 메서드는 항시 RegexResults를 리턴할 것이며, 당신은 regex가 일치했는지, 일치결과(match) 또는 컬렉션으로서 다른 레지스터 그룹의 내용과 위치 Interval, 기타 다른 기능을 문의할 수 있다. 예를 들어, 아래는 단순한 구성 파일 행 파서이다:

| file config |
config := LookupTable new.
file := (File name: myapp.conf) readStream.
file linesDo: [:line |
    (line =~ (\w+)\s*=\s*((?: ?\w+)+)) ifMatched: [:match |
        config at: (match at: 1) put: (match at: 2)]].
file close.
config printNl.


Perl과 마찬가지로 +~는 전체 문자열을 스캔하여 일치하는 대상을 발견하면 가장 왼쪽의 결과를 응답하는데, 그 위치로부터 가능한 한 많은 문자를 사용한다. #matchRegex: 와 같은 변형메시지(variant message)로 검색을 할 수도 있으며, ^ 혹은 $의 일반적인 구문을 선호한다면 그것을 사용해도 좋다.


문자열에서 일치하는 특정 RegexResults 객체를 유효하게 유지하고 싶다면 해당 문자열을 수정해선 안 되는데, 일치하는 텍스트에 대한 변경사항이 RegexResults 객체까지 전파될 수 있기 때문이다.


Perl의 연산자와 비슷하게 GNU Smalltalk 또한 #replacingRegex:with: 를 제공한다. Perl과 다른 점은 이 때 GNU Smalltalk는 #% 메시지의 패턴 확장 구문을 이용한다는 점이다. 가령 'The ratio is 16/9.' replacingRegex: '(\d+)/(\d+)' with: '$%1\over%2$' 는 'The ratio is $16\over9$.' 라고 응답한다. g 변경자 대신 #replacingAllRegex:with: 메시지를 사용하라.


또 다른 흥미로운 String 메시지로 #onOccurrencesOfRegex:do: 가 있는데, 이 메시지는 수신자에서 발견되는 일치결과(match)마다 두 번째 인자인 블록을 호출한다. 내부적으로 모든 검색은 이전에 성공한 일치결과의 마지막부터 시작될 것이다. 예를 들어, 아래는 스트림에 모든 단어를 인쇄할 것이다:

stream contents onOccurrencesOfRegex: ’\w+
            do: [:each | each match printNl]


네임스페이스

[해당 절은 (GNU Smalltalk에서 네임스페이스의 구현) Augustin Mrazik의 연구인 Structured Symbolic Name Space in Smalltalk 를 기반으로 한다.]

서론

GNU Smalltalk 가 역사적으로 기반을 둔 Smalltalk-80 프로그래밍 환경은 하나의 전역적 네임스페이스에서-Smalltalk 시스템 사전-객체의 상징적 식별(symbolic identification)을 지원한다. 즉, 시스템 내 각 전역 변수는 소스 코드에서 (예: 표현식 또는 메서드 내) 특정 객체의 상징적 식별에 사용되는 유일한 이름을 갖고 있다는 의미다. 그 중에서 가장 중요한 전역 변수는 객체의 행위를 정의하는 클래스들이다.


실제 시스템의 모델링 처리를 위한 개발 시에는 종종 다형적 상징적 식별이 필요하다. 그말인즉슨, 여러 클래스 또는 다른 전역 변수에 동일한 이름을 사용하는 것이 가능해야 한다는 의미다. 적절한 변수 바인딩의 선택은 컨텍스트에 따라 좌우되어야 한다. 설명을 위해 Statement 클래스를 예로 들어볼 것인데, 이는 다른 영역에서는 완전히 다른 의미를 가질 것을 기억하라:


GNU Smalltalk 또는 다른 프로그래밍 언어

코드 전체의 최상위에 있는 표현식, 대입(assignment) 또는 요소분기(branching)처럼 이용할 수 있는 특수 구문도 함께 존재할 수 있다.

Bank

고객의 최근 거래내역에 대한 추적 보고서

AI (논리 인출)

논리 시스템 내 true 검증


추후 재개를 위해 각 세션 후마다 저장하도록 ObjectMemory 스냅숏을 이용하여 지속적으로 작업을 시작할 경우 이 문제는 불가피해진다. 예를 들어, 이미지에 위와 같은 의미를 지닌 "Bank"와 함께 Statement 클래스가 이미 존재하는데 (예: 우리 이미지에서 실행하는 라이브 은행 지원 시스템 내), YAC[Yet Another C]의 개발을 시작하기로 결정할 수도 있다. 컴파일러에 대한 파스 노드를 작성하기 시작하면 #Statement가 뱅킹 패키지(banking package)에 묶여(bound to) 있음을 발견할 것이다. 이것을 자신의 파스 노드 클래스로 대체할 수 있는데, 그러면 bank의 Statement는 완전한 기능의 언바운드(unbound) 클래스로서 시스템에 남아 있을 수 있다; 하지만 소스 코드내에 상징적 수준에서 더 이상 접근할 수 없다. 이러한 점이 문제가 될 것인지 아닌지는 bank의 코드에서 Statement 클래스를 참조하는지, 이러한 참조가 언제 발생하는지에 따라 좌우된다.


소스 코드에서 이름으로 식별해야 하는 객체는 SystemDictionary의 유일한 인스턴스인 Smalltalk에 포함되어 있다. 그러한 객체들은 여느 변수명과 마찬가지로 당신이 이름을 쓰면 식별할 수 있다. 코드는 기본 환경에서 컴파일되고, 변수가 클래스 풀 또는 지역 변수에 의해 숨겨지지 않고 Smalltalk에서 발견될 경우 그 값이 검색되어 표현식의 값으로 사용된다. 이렇게 Smalltalk는 유일한 상징적 네임스페이스를 표현한다. 다음 단락에서는 좀 더 분명한 이해를 위해, 개념으로서의 상징적 네임스페이스를 간단하게 환경이라고 부를 것이다.


개념

다형적 상징 식별(polymorphic symbolical identification)을 지원하기 위해서는 여러 개의 환경이 필요하다. 동일한 이름이 여러 환경에서 키(key)로 동시에 존재하여 각 환경에서 다양한 객체를 가리키기도 한다.


이러한 환경들 간에는 상징적 탐색이 필요하다. 구현해야 할 구문(syntax)과 의미(semantics)의 문제에 대한 접근법을 구현하기 전에 우선 환경들 간에 구축할 구조적 관계를 결정해야 한다.


환경은 먼저 그 전역 변수로의 접근을 명령하는 것으로 상징적으로 식별되어야 하므로 그 자체가 다른 환경의 전역 변수여야 한다. Smalltalk는 다른 환경들과 그 변수들의 선택을 시작하는 루트 환경으로 선택하기에 훌륭한 환경이다. Smalltalk에서 기존에 존재하던 하위 환경이 보일 수도 있다; 또 그것의 하위 환경들도 보일 수 있다. 이는 환경들이 노드를 그래프로 표현하는데, 한 환경으로부터 다른 환경으로의 상징적 선택이 branch를 나타냄을 의미한다.


상징적 식별은 다형적일 테지만 모호해선 안 된다. 이것이 바로 환경 그래프에서 사이클(cycle)을 피해야 하는 이유다. 그래프상에 원이 있으면 구현에 다른 문제를 야기하기도 하는데, 가령 재귀적 알고리즘의 이용 불가성을 예로 들 수 있겠다. 따라서 일반적으로 환경은 방향성 비사이클 그래프를 빌드해야 한다; GNU Smalltalk는 최근에 환경을 pool dictionary로서 사용 가능한 추가 기능이 있는 n-ary 트리로 그래프를 제한한다.


환경들 간에 발생하는 부분 순서 관계인 상속을 상기시켜보자. 하위 환경은 상위 환경으로부터 상속된다. 객체 지향성의 의미에서 상속의 기능은 이러한 관계와 연관된다: 상위 환경의 모든 연관관계는 그 하위 환경에서도 유효하나, 하위 환경에서 부분적으로 재정의된 경우는 제외된다.


상위 환경은 그 이름 밑에 있는 Associations와 같은 하위 환경을 모두 포함한다. 하위 환경은 #Super 기호 밑에 있는 상위 환경을 포함한다. 대부분 환경은 표준 루트 환경인 Smalltalk로부터 상속되지만 꼭 그래야 하는 것은 아니다; 대부분의 클래스가 Object로부터 파생되지만 nil 에서 직접 클래스를 파생할 수도 있다는 맥락과 비슷하다. 이러한 환경들은 모두 Smalltalk의 전역 변수를 상속하므로 굳이 각 환경에서 Smalltalk의 Smalltalk를 가리키는 Smalltalk를 정의하지 않아도 된다.


상위 환경에 대한 상속 연결은 잠재적으로 상속되는 전역 변수의 검색에 사용된다. 여기에는 #at: 과 #includesKey: 와 같은 메서드를 통한 검색이나 컴파일러의 변수 바인딩 검색이 포함된다.


구문

지역적이든 상속적이든 상관없이 환경에 대한 전역 변수는 소스 코드에 사용되는 그것의 기호 변수명에 의해 참조될 수 있는데, 예를 들자면 다음과 같다:

John goHome


특정 환경이나 그 최상위 환경(super-environment) 중 하나에 #John ->aMan 관계가 존재하는 경우 루트 환경까지 이어진다.


객체를 다른 환경에서부터 참조해야 하는 경우 (예: 하위 환경에 속하지 않는 경우) Super 기호를 이용해 현재 환경의 위치와 비교하거나 객체의 "전체 경로명"을 이용해 절대적으로 참조하여 하위 환경의 트리를 통해 트리 루트(주로 Smalltalk)부터 검색하는 방법이 있다.


다른 환경에서 전역 변수를 식별 시 우리는 기호의 "경로명"을 사용한다. 기호는 마침표로 구분한다; 따라서 그 모양은 아래와 같다.

Smalltalk.Tasks.MyTask
Super.Super.Peter.


스몰토크에서 언제나처럼 전역 변수로 접근 시 대문자를 사용해야 함을 상기시킬 필요가 있다. 또 다른 구문은 변수 바인딩, 즉 특정 전역 변수에 관한 Association을 리턴한다. 위의 첫 예제와 같이:

#{Smalltalk.Task.MyTask} value


뒤에 나오는 구문, 변수 바인딩은 리터럴 배열 내에서도 유효하다.


구현

SystemDictionary의 슈퍼클래스인 RootNamespace가 정의되며, Smalltalk-80 SystemDictionary의 기능 다수가 그 클래스에 의해 관리될 것이다. Namespace와 RootNamespace는 AbstractNamespace의 서브클래스에 해당한다.


상속을 처리하기 위해서는 아래 메서드를 (RootNamespace가 아니라) Namespace에서 정의 또는 재정의해야 한다:

#at:ifAbsent: 혹은 #includesKey: 와 같은 접근자

상속을 구현해야 한다. 변수 읽기를 시도하는 Namespace가 고유의 dictionary 혹은 최상위 환경 사전에서 연관관계(association)을 찾을 때 이것을 사용한다; Dictionary의 쓰기와 새 연관관계를 생성해야 하는 경우, Namespace는 자체 dictionary에 그것을 생성한다. 최상위 환경에서 바인딩이 관련 변수의 바인딩일 경우 그것의 수정이 가능하도록 해주는 #set:to:와 같은 특수 메서드가 있다.


#do: 혹은 #keys 와 같은 열거자

상속된 객체를 포함해 네임스페이스 내 모든 객체를 리턴할 것이다.


계층구조 접근

AbstractNamespace는 네임스페이스 계층구조의 탐색을 허용하는 새로운 메서드 집합도 구현할 것이다; 이는 클래스 계층구조의 Behavior에서 발견되는 것과 유사하다.

Namespace 클래스의 가장 중요한 작업은 Smalltalk 시스템에서 가장 중요한 전역 객체-클래스-를 위한 조직(organization)을 제공하는 일이다. 그 중요성은 그러한 클래스를 위해 컴파일된 코드의 의미를 변경하도록 되어 있는 다중 환경의 구조에서 특히 중요해진다.


스몰토크에서 클래스는 클래스의 이름을 보유하는 인스턴스 변수 name을 갖는다. 정의된 클래스 각각은 name이란 이름으로 Smalltalk 또는 다른 환경에 포함되어 있다. 여러 환경을 가진 프레임워크의 경우 클래스는 자신이 생성되고 컴파일된 환경을 알고 있어야 한다. 이는 관련 메서드에서 정의되고 적절히 사용되어야 하는 Class의 새 속성이다. Mother 환경에서 클래스는 자체 이름 아래에 포함될 것이다.


여느 객체와 마찬가지로 모든 클래스는 여러 환경에 동시에 포함될 수도 있으며, 심지어 동일한 환경 또는 다양한 환경에서 다른 기호 하에 존재할 수도 있다. 우리는 특정 클래스의 이러한 "별명(alias name)" 또는 다른 값을 고려할 수 있다. 클래스는 인스턴스 생성 또는 클래스로의 메시지를 목적으로 mother 환경과는 다른 환경 내에서 또는 다른 이름으로 참조되기도 하지만 다른 환경에서 컴파일을 요청한다 하더라도 이러한 환경에서 코드를 컴파일해서는 안 된다. Mother 환경에서 구문이 올바르지 않을 경우 컴파일 오류가 발생한다. 클래스는 고유의 메서드를 컴파일할 책임이 있으므로 이러한 오류는 "mother 환경" 클래스의 존재로 인한 것이다.


다양한 도구에서 (예: 브라우저) 클래스 식별을 목적으로 하여 클래스가 응답한 클래스의 이름도 문제가 된다. 클래스 이름이 표시되는 환경을 반영하도록 변경되어야 하는데, 예를 들자면 'nameIn: environment' 메서드는 적절한 위치에서 구현되고 사용되어야 한다.


구조화된 환경의 전체 기능을 얻기 위해서는 스몰토크 시스템에도 변경사항이 있어야 한다. 특히 행위(behavior) 클래스, 사용자 인터페이스, 컴파일러, 지속성(persistence)을 지원하는 소수의 클래스를 변경해야 한다. 한 가지 짚고 넘어갈 점은 UndefinedObject 상에서 메서드를 컴파일함으로써 구현되는 REPL 또는 "Workspace"에서 이루어지는 평가는, 설사 mother 환경이 Smalltalk라 하더라도 UndefinedObject의 환경이 "현재 환경"이고 Namespace current에 의해 접근 가능하다면 더 나을 것이다.


네임스페이스 사용하기

네임스페이스의 사용은 PackageLoader이 사용하는 GNU Smalltalk XML 패키지 설명에 'namespace' 옵션을 추가하거나, 아래와 같이 코드를 래핑하는 문제에 불과하다:

Namespace current: NewNS [
    ...
]


네임스페이스는 아래와 같이 클래스로 불러올 수 있다:

Stream subclass: EncodedStream [
    <import: Encoders>
]


이 방법이 아니라면 네임스페이스 내에 클래스 경로를 (그리고 다른 객체의 경로를) 완전히 명시해야 할 것이다. 네임스페이스를 클래스로 가져오는 것은 클래스의 적절한 정의부에서 이루어지는 C++의 using namespace 선언과 비슷하다.


마지막으로 기본 시스템 클래스로 작업 시에는 주의를 기울여야 한다.

Namespace current: NewNS [
    Smalltalk.Set subclass: #Set [
        <category: ’My application-Extensions’>
        ...
    ]
]


위와 같은 코드를 사용할 수도 있겠지만, 이러한 접근법은 핵심 클래스(core class)에 적용하면 효과가 없을 것이다. 예를 들어, Set 또는 WriteStream 객체로는 성공할지 모르지만 SmallInteger를 이러한 방식으로 서브클래싱할 경우 이상한 결과를 야기한다: 정수 리터럴이 여전히 클래스에 대한 Smalltalk dictionary의 버전에 속하고 (Arrays, Strings 등도 마찬가지다), 원시 연산은 여전히 표준 Smalltalk SmalltalkIntegers를 응답하게 될 것이다. 이와 비슷하게 단어 모양(word-shaped)은 32-bit Smalltalk.LargeInteger 객체를 인식하지만, 당신의 네임스페이스에 속한 LargeInteger는 인식하지 못할 것이다.


불행히도 이 문제는 해결하기가 쉽지 않은데 스몰토크가 속도를 위한 확정적(determinate) 클래스 객체의 OOP를 알아야 하기 때문이다-Integer로 + 메시지가 전송될 때마다 메시지의 전송자가 속한 환경을 검색하기란 불가능하다.


따라서 GNU Smalltalk 네임스페이스는 아직 클래스로의 확장자(extensions) 간 캐시 문제를 100% 해결할 수 없다-여전히 메서드명에 붙은 접두사에 의존해야 한다. 하지만 클래스 이름 간 캐시 문제나 클래스명과 pool dictionary 이름 간 캐시 문제는 해결해준다.


네임스페이스는 패키지와는 무관하다; 패키지를 로딩하더라도 그에 상응하는 네임스페이스를 가져오지는 않는다.


디스크 파일-IO primitive 메시지

완전히 객체 지향적인 방식으로 파일을 생성하고 파일 시스템에 접근하도록 해주는 클래스가 네 가지 있다 (FileDescriptor, FileStream, File, Directory).


FileDescriptor와 FileStream은 C 언어에서 제공하는 것보다 훨씬 더 강력하다 (두 언어 간 차이는 C stdio 라이브러리와 같이 FileStream도 버퍼링을 한다). 우선은 원(raw) 이진 데이터를 portable endian-neutral(이식성 엔디안 중립적) 포맷으로 작성하도록 해준다. 그렇지만 이보다 더 중요한 점은 이러한 클래스들이 가상 파일시스템과 비동기식 I/O을 투명하게 구현한다는 사실이다.


비동기식 I/O란 입/출력 연산이 다른 것들은 제외하고 그것을 실행하는 Smalltalk Process만 막기 때문에 네트워크 프로그래밍 배경에 매우 유용하다. 가상 파일 시스템은 이러한 객체들이 tar나 gzip 파일과 같은 보관소로부터 파일을 투명하게 추출할 수 있는데, 이는 스몰토크 프로그래밍 또는 셸 스크립팅을 통해 확장 가능한 메커니즘을 통해서 이루어진다. 이러한 클래스에 관한 더 많은 정보는 VFS 네임스페이스 하에서 클래스 참조를 참고한다. URLs를 파일명으로 사용해도 좋다; 하지만 NetClients 패키지를 로딩하지 않은 이상 (43 페이지의 3.8절 [네트워크 지원] 참조) file URL만 허용될 것이다.


뿐만 아니라 stdin, stdout, stderr, 이 세 가지 파일이 FileStream의 전역 인스턴스로 선언되는데, 이는 C 가상 머신으로 전달되면서 적절한 값에 결부(bound to)된다. 이는 stdout 또는 FileStream stdout로서 접근 가능한데, 전자는 타이핑하기가 수월하고 후자는 의미 전달이 명확하다.


마지막으로 Object는 네 가지 다른 메서드를 정의한다: print와 printNl, store과 storeNl. 이 메서드들은 "Transcript" 객체로 printOn: 또는 storeOn:을 실행한다; 이 객체는 TextCollector 클래스의 유일한 인스턴스로서 주로 쓰기 연산을 stdout로 위임한다. 대신 Blox GUI를 로딩할 경우 Transcript Window가 Transcript 창에 부착될 것이다 (34 페이지의 3.1절 [Blox] 참조).


파일명을 문자열 인자로 하여 FileStream 클래스로 전송된 fileIn: 메시지는 파일을 스몰토크로 로딩시키도록 할 것이다.

FileStream fileIn: 'foo.st'!


위의 경우 foo.st 는 GNU Smalltalk로 로딩될 것이다.


GNU Smalltalk ObjectDumper

또 다른 GNU Smalltalk 특정적인 클래스로 ObjectDumper 클래스가 있는데, 이는 객체를 이식 가능, 엔디안 중립적(endian-neutral), 이중 포맷으로 버리도록 해준다. 또 다른 GNU Smalltalk 특정적 클래스, 즉 디스크 파일을 취급하는 것과 동일한 방식으로 ByteArrays를 처리하게 해주는 ByteStream 덕분에 ObjectDumper를 ByteArrays에도 사용할 수 있다.


ObjectDumper의 사용에 관한 상세 정보는 클래스 참조를 살펴보라.



동적 로딩

DLD 클래스는 해결되지 않은 함수를 자동으로 일련의 프로그램 특정적 라이브러리로 검색하도록 C callout 메커니즘을 향상시킨다. 목록에 라이브러리를 추가하려면 아래와 같이 코드를 계산하라:

DLD addLibrary : 'libc'


확장자(운영체제별로 .so, .sl, .a, .dll)는 자동으로 추가될 것이다. 이식성을 위해 사용자 스스로 명시하지 않도록 한다.


그러면 C 런타임 라이브러리에 모든 함수를 저장하기 위해 표준 C call-out 메커니즘을 사용할 수 있다. 이는 잠재적 보안 문제이므로 (특히 자신의 프로그램이 Unix에서 SUID 루트인 경우) GNU Smalltalk를 확장 언어로 사용할 때에는 동적 로딩의 비활성화를 원할지도 모른다. 동적 로딩을 비활성화하려면 --disable-dld 스위치를 전달하는 GNU Smalltalk를 구성하라.


동적 로딩이 비활성화 되더라도 DLD 클래스는 존재할 것이지만 (사용자 시스템이 지원되지 않거나 --disable-dld 구성 스위치에 의해) 동적 연결을 수행하려는 시도는 모두 오류를 야기할 것이다.


자동 문서 생성기

GNU Smalltalk는 gst-doc 명령을 통해 호출되는 자동 문서 생성기를 포함한다. 코드는 사실 ClassPublisher 패키지의 일부이며, gst-doc가 문서화될 코드를 읽고 ClassPublisher를 촉발한다.


현재 gst-doc은 Texinfo 포맷으로만 출력을 생성하지만 향후 배포되는 제품에서는 변경될 것이다.


gst-doc는 이미 이미지에 있는 코드를 문서화하거나, 로컬 외부 파일이나 패키지를 로딩할 수 있다. 후자 접근법의 경우 코드 또는 파일을 다른 파일/패키지에서 자동으로 생성하는 파일이나 패키지에는 작용하지 않을 것이다.


gst-doc은 아래와 같이 호출된다:


gst-doc [flag ...] class ...

아래 옵션이 지원된다:


-p package

--package=package

package 패키지 내에 있는 클래스에 관한 문서를 생성한다.


-f file

--file=file

file 파일 내에 있는 클래스에 관한 문서를 생성한다.


-I

--image-file

주어진 이미지에 이미 존재하는 코드에 관한 문서를 생성한다.


-o

--output=file

명명된 파일 내에서 문서를 방출(emit)한다.


클래스라 함은 클래스명이거나 네임스페이스 이름 다음에 .*가 붙은 대상이다. 문서는 명령 행에 명시된 클래스와 관련해 작성될 것이다. –f 또는 –p 옵션이 주어질 경우 클래스는 생략될 수 있다. 이런 경우 문서는 패키지 내 모든 클래스와 관련해 작성될 것이다.


메모리 접근 메서드

GNU Smalltalk는 고유의 내부 데이터 구조를 문의할 수 있는 메서드를 제공한다. 당신은 객체의 실제 메모리 주소, 혹은 주어진 객체를 가리키는 OOP 테이블의 실제 메모리 주소를 결정할 수 있는데, 아래와 같이 Memory 클래스에 메시지를 이용하여 가능하다:


asOop [Object 상의 메서드]

anObject에 대한 OOP의 색인을 리턴한다. 해당 색인은 쓰레기 수집으로부터 안전하며, 기본적으로 anObject에 대한 해시 값으로 사용되는 것과 동일한 값이다 (Object의 has와 identityHash 구현에 따라 리턴된다).


asObject [Integer 상의 메서드]

(주소가 아니라) 주어진 OOP 색인을 객체로 다시 변환한다. 주어진 색인에 어떤 객체도 연관되지 않은 경우 실패한다.


asObjectNoFail [Integer 상의 메서드]

(주소가 아니라) 주어진 OOP 색인을 객체로 다시 변환한다. 주어진 색인에 어떤 객체도 연관되지 않은 경우 nil을 리턴한다.


ByteArray와 Memory 내 다른 메서드들도 다양한 C 타입을 (doubleAt:, ucharAt: 등) 읽을 수 있도록 해준다. asOop과 asObject의 사용 예제를 보려면 blox-tk/bloxbasic.st에서 Blox 소스 코드를 살펴보라.


또 다른 흥미로운 클래스로 ObjectMemory가 있다. 이는 가상 머신의 메모리 사용을 조정하도록 해주는 메서드를 몇 가지 제공한다; 과거에 Smalltalk의 인스턴스 메서드였거나 Memory의 클래스 메서드에 해당했던 메서드 다수는 현재 ObjectMemory의 클래스 메서드에 속한다.


게다가 이번 절 마지막으로 살펴볼 내용은 가상 머신이 정확히 이 클래스를 통해 그 종속자(dependents)들에게 이벤트를 전송한다는 점이다.


수신이 가능한 이벤트는 다음과 같다.

returnFromSnapshot

이미지가 재시작될 때마다 전송되며, 기존 버전에서 init block의 개념을 대체한다.


aboutToQuit

해석기(interpreter)가 존재하기 이전에 전송되는데, ObjectMemory quit이 전송되었거나 명시된 파일이 모두 파일 인(file in) 되었기 때문에 발생한다. 해당 이벤트 내부에서 벗어나면 무한 루프를 발생할 수 있으니 유의하기 바란다.


aboutToSnapshot

이미지 파일이 생성되기 직전에 전송된다. 해당 이벤트 내부에서 벗어나면 기존에 있던 이미지는 원본 그대로 남겨둘 것이다.


finishedSnapshot

이미지 파일이 생성된 직후에 전송된다. 해당 이벤트 내부에서 벗어나면 이미지를 사용 불가한 상태로 만들지 않을 것이다.


GNU Smalltalk에서 메모리 관리

GNU Smalltalk 가상 머신에는 쓰레기 수집기가 구비되어 있는데, 이 기능은 시스템 루트로부터 더 이상 접근할 수 없는 객체가 차지하는 공간을 회수한다. 수집기는 여러 부분으로 구성되는데, 각 부분은 조정이 가능한 다양한 전략을 이용해 가상 머신에 의해 호출되기도 하고 프로그래머에 의한 수동 호출도 가능하다.


이러한 부분으로는 generation scavenger, 점진적 정리 단계(incremental sweep phase)가 있는 mark & sweep 수집기, 그리고 compactor가 있다. 이 모든 기능들은 서로 다른 메모리 공간을 작업하고, 그 범위, 속도, 단점에 (여러 알고리즘의 가용성으로 이러한 단점의 균형을 맞출 수 있길 바랄 뿐이다) 있어서 차이가 있다. 따라서 이러한 알고리즘에 대한 설명과 알고리즘이 작동하는 메모리 공간을 설명하겠다.


NewSpace는 객체가 상주하는 메모리 공간으로, 세 가지 하위 공간으로 구성된다: 하나는 객체 생성 공간(Eden), 나머지 두 개는 SurvivorSpaces 라 부른다. 객체가 처음으로 생성되면 Eden이라는 공간에 위치한다. Eden이 채워지기 시작하면 (예: Eden에서 사용된 바이트 수가 scavenge 한계선을 초과 시), 객체는 Eden 또는 공간이 차지된(occupied) SurvivorSpace에 갇히는데, 여전히 시스템 루트로부터 접근할 수 있는 객체들은 차지되지 않은(unoccupied) SurvivorSpace로 복사된다. 객체가 여러 scavenging pass를 살아남으면서 공간이 차지된 SurvivorSpace로부터 차지되지 않은 SurvivorSpace로 scavenger에 의해 끌려갈 것이다. SurvivorSpace에서 사용된 바이트 수가 충분히 높아서 scavenge 중지(pause) 시간이 너무 길 가능성이 있는 경우 scavenger는 NewSpace에서 오래된 라이브 객체 일부를 OldSpace로 이동시킬 것이다. 쓰레기 수집 용어로 말하자면 그러한 객체들은 OldSpace로 tenured 된다.


이러한 쓰레기 수집 알고리즘은 단수명 객체, 즉 NewSpace에서 상주하는 동안 만료(expired)되는 객체들을 회수하고, NewSpace에 충분한 데이터가 상주하여 그 중 일부를 OldSpace로 이동시키기 유용한 시기를 결정하도록 고안되었다. Copying 쓰레기 수집기는 특히 그 구성원이 생존할 가능성 보다는 죽을 가능성이 높은 객체 무리에서 효율적인데, 이러한 유형의 scavenger는 결국 수가 많은 시체(corpse)를 추적하기보다는 몇 안 되는 라이브 객체를 복사하는 데에 대부분의 시간을 소비하기 때문이다. 이러한 사실 때문에 컬렉션 복사는 특히 NewSpace에 알맞은데, NewSpace에서는 90% 이상의 객체들이 단일 scavenge에 걸쳐 생존을 실패하기 때문이다.


NewSpace의 특정 구조는 많은 단점을 가진다. 첫 번째로, 거대한 규모의 Eden과 두 개의 작은 SurvivorSpaces을 갖는 것은 두 개가 동일하게 큰 반 공간(semi-space)을 갖고 공간이 차지된 공간으로부터 새 객체를 직접 할당하는 방법보다 (기본적으로 GNU Smalltalk는 420=300+60*2 KB 메모리를 사용하는 반면 좀 더 간단한 구성은 720=360*2 KB를 사용한다) 메모리 사용량(memory footprint)이 적기 때문이다. 반면 이는 tenuring 결정을 특히 간단하게 만든다: 복사 순서는 단수명 객체가 마지막으로 복사되고 OldSpace로부터 참조되는 객체가 먼저 복사되는 경향이 있다: 그 이유는 scavenger의 tenuring 전략이 SurvivorSpace 목적지를 단순히 원형 버퍼로 취급하여 선입 선출(First-In-First-Out) 규칙으로 객체를 tenure하기 때문이다.


객체는 여러 가지 이유로 scavenger root set의 일부가 될 수도 있다: tenured된 객체의 데이터가 마지막 scavenge로 작성된 OldSpace 페이지에 상주하는 경우 해당 객체는 루트 객체(roots)이고 (관련 내용은 추후 상세히 다루겠다), C 코드 또는 스몰토크 스택에서 참조하는 것으로 알려진 모든 객체들도 루트 객체가 될 수 있다.


결국 오래된 객체들 중 일부는 FixedSpace 라 불리는 특별한 영역에서 상주하도록 만들 수 있다. FixedSpace에 상주하는 객체는 그 본체(body)가 고정 주소에 남도록 보장되어 있다는 점에서 특별하다 (일반적으로 GNU Smalltalk는 객체의 헤더가 Object Table 내의 고정된 주소에 남도록 보장할 뿐이다). 쓰레기 수집기는 객체를 이동 가능하며 실제로 이동시키기도 하는데, 그러한 이동은 객체의 주소를 고정 키(fixed key)로 이용하거나 ByteArray를 버퍼로서 이용하는 외부 코드(foreign code)로 객체를 전달하여 이루어진다. 사실상 이동하지 않는 malloc 힙에서 CObject를 이용해 C 데이터를 조작할 수도 있으나, 이는 지루한 방식으로, 메모리 누수를 피하기 위해서는 C에서의 코딩할 때에 버금가는 집중력을 요한다. FixedSpace는 훨씬 더 편리한 메커니즘을 제공한다: 객체가 고정되었다고 간주되면 객체의 본체(body)는 그 수명이 다할 때까지 절대 이동하지 않을 것이다; 단, 객체가 차지하는 공간은 객체가 쓰레기 수집될 때 FixedSpace pool로 자동 반환될 것이다. FixedSpace에 있는 객체는 이동할 수 없으므로 FixedSpace는 압축될 수 없고, 따라서 광범위한 단편화로 고통받을 수 있다. 이러한 이유로 FixedSpace는 조심스럽게 사용해야 한다. 하지만 FixedSpace는 스냅숏을 저장하고 종료한 후 새로 저장된 이미지를 재시작함으로써 확보할 수도 있겠다.


OldSpace와 FixedSpace의 공간은 시스템 할당자 malloc의 변형체(variation)을 이용해 할당된다: 사실상 GNU Smalltalk는 동일한 할당자를 이용해 OldSpace와 FixedSpace에 대한 자신의 내부적 요구에도 사용하지만, 주어진 메모리 페이지가 구분된 공간에 상주하는 객체는 절대로 host하지 않도록 보장한다. 새 페이지가 필요 시 주소 공간으로 매핑되고 OldSpace 또는 FixedSpace segment로 충당된다; 사용하지 않을 때에도 잇따라 언매핑(unmapped)되거나, 다른 스몰토크 데이터 공간 또는 malloc에서 재사용될 때까지 대기하면서 남겨질 수도 있다.


오래된 객체들 간에 생성되는 쓰레기는 mark & sweep 수집기에 의해 처리되는데, 이는 NewSpace 내의 객체만 회수하는 scavenger과는 달리 OldSpace 내 객체들도 회수할 수 있다. 객체가 할당될 때는 살아 있던 객체들이 Eden에서 이전에 차지했던 공간을 사용할 뿐만 아니라 scavenger가 회수할 수 있었던 객체가 해제시킨 전역 Object Table 내 엔트리도 재사용할 수 있다. Free 객체 테이블 엔트리에 대한 요청은 OldSpace 수집기의 sweep 단계와 결합하여 점진적으로 실행함과 동시 OldSpace 쓰레기 수집의 파괴적 부분을 mark 단계로 제한하는 것이 가능하다.


Mark & sweep을 여러 번 실행하면 단편화로 이어지기도 한다 (그러면 객체가 여러 페이지로부터 할당되고, 다수의 객체가 각 페이지에 남아 시스템이 이를 재활용 할 수 없는 순서로 쓰레기가 되어 버린다). 이러한 이유로 시스템은 주기적으로 OldSpace의 압축(compact)을 실행한다. 시스템은 오래된 객체를 모두 루프 스루하고 새 OldSpace로 복사함으로써 이를 실행한다. OldSpace 할당자는 객체가 해제되기 시작할 때까지, 그리고 모든 객체가 해제되기 전까지는 단편화로 고통받지 않으므로, 복사가 끝날 무렵 단편화된 OlsSpace 내 모든 페이지는 시스템으로 반환되어 있을 것이고 (그 중 일부는 압축된 OldSpace에서 이미 사용하고 있을 것이다), 새롭고 압축된 OldSpace는 이미 시스템 오래된 공간(system oldspace)로서 사용되고 있을 것이다. 객체 힙을 증가시키면 (mark & sweep 수집이 끝난 후에도 꽤 차 있는 것(full)으로 발견되면 실행된다) 자동으로 압축을 트리거한다.


라이브 객체를 표시하지 않고 compactor를 실행할 수 있다. OldSpace 내 쓰레기의 양은 주로 꽤 제한되어 있기 때문에 잠재적으로 죽은 객체를 복사 시 발생하는 오버헤드는 충분히 작아서 compactor가 여전히 전체 쓰레기 수집보다 상당히 빠르게 실행되며, 여전히 애플리케이션에 어느 정도 숨쉴 공간을 제공한다.


OldSpace와 FixedSpace를 동일한 힙(heap)에 유지하는 경우 OldSpace의 (단편화을 제한하기 위해 수시로 재빌드되는) 압축을 훨씬 덜 효과적으로 만들 것이다. 또한 malloc 힙은 FixedSpace 객체에 사용되지 않는데, GNU Smalltalk가 어린(young) 객체의 효율적인 scavenging을 지원하기 위해서는 OldSpace와 FixedSpace에 발생하는 쓰기(writes)를 추적할 필요가 있기 때문이다.


이를 위해 회색 페이지 테이블[2]은 NewSpace에 갇힌 객체로의 참조를 최소한 하나라도 포함하는 것으로 고려되는 Fixed Space 또는 OldSpace 내 각 페이지마다 하나의 엔트리를 포함한다. OldSpace 내 각 페이지는 회색으로 생성되고, 그것이 사실상 NewSpace로의 포인터를 포함하지 않는다는 사실을 scavenging pass에서 발견할 때까지 회색으로 간주된다. 이후 페이지는 다시 검정색으로 칠해지고,[3] 그것이 작성되거나 다른 객체가 페이지 안에 할당될 때까지 (새 고정 객체 또는 tenured되는 어린 객체) 검정색으로 남는다. 회색 페이지 테이블은 가상 머신이 필요로 할 때마다 확장되고 축소된다.


객체 크기의 히스토그램을 그려보면 평균적으로 큰 객체의 (예: 페이지보다 크기가 큰 객체) 소스는 소수에 불과하지만 그 중 동적으로 생성되어 특별하게 처리되어야 하는 객체들의 수도 충분하다는 사실을 보여준다. 그러한 객체는 일반 객체와 함께 NewSpace에서 할당되어야 하는데, NewSpace를 너무 일찍 채울 것이며 (혹은 들어맞지 않을지도 모른다), 그에 따라 scavenging 속도는 가속화되고 성능은 감소되어 tenured 쓰레기의 양이 증가할 것이다. 이는 객체가 생성되는 시기에 이러한 객체들을 효과적으로 tenure시키기 때문에 최적의 해결책은 아니지만, 이러한 객체들을 FixedSpace에 바로 할당하면 어느 정도 이익을 취할 수도 있다. FixedSpace가 사용되는 이유는 이러한 객체들이 단편화를 야기하지 않을 만큼 충분히 크기 때문이다;[4] 그리고 OldSpace 대신 FixedSpace를 사용하면 단편화 감소와 관련해 어떠한 이익도 제공하지 않기 때문에 compactor의 복사를 피할 수 있다.


스몰토크 활성화 레코드는 다른 특별한 힙(heap)인 컨텍스트 풀(context pool)로부터 할당된다. 이는 주로 후입 선출(Last-In-First-Out; 스택) 방식으로 할당 해제될 수 있어서 객체 테이블에 그들을 위한 엔트리를 할당하는 데 필요한 작업을 저장하고 그들이 사용하는 메모리를 재빠르게 재사용하기 때문이다. 하지만 스몰토크가 활성화 레코드로 접근하면 활성화 레코드는 일급(first-class) OOP[5]로 바뀌어야 한다. 이러한 객체들조차 주로 매우 수명이 짧으므로 데이터는 Eden으로 복사되지 않는다: 컨텍스트 풀로부터 객체 본체(bodies)가 제거된 다음 scavenging으로 연기되고, 이 또한 Eden을 비우는 것과 마찬가지로 컨텍스트 풀을 비울 것이다. 소수의 객체가 할당되고 컨텍스트 풀이 Eden보다 먼저 채워지면 scavenging도 트리거된다; 하지만 이는 매우 드문 경우다.


선택적으로 GNU Smalltalk는 메서드가 기본이 되는 마이크로프로세서의 머신 코드로 컴파일되고 난 후에야 주어진 스몰토크 메서드를 실행함으로써 해석의 오버헤드를 피할 수 있다. 이러한 머신 코드 생성은 자동으로 실행되고, 결과가 되는 머신 코드는 malloc이 관리되는(malloc-managed) 메모리에 위치한다. 우선 실행하고 나면 메서드의 머신 코드는 잇따른 실행을 위해 그 곳에 남겨진다. 하지만 모든 스몰토크 메서드의 머신 코드 버전을 영구적으로 수용하기에는 너무 많은 메모리가 소요되므로 메서드는 한 번 이상 컴파일될지도 모른다: 두 번의 쓰레기 수집 액션을 취하는 시기에 (scavenges와 전역 쓰레기 수집은 동일하게 계수) 번역이 사용되지 않은 경우 증분 sweeper가 그것을 폐기하므로 필요 시에만 재계산할 것이다.


GNU Smalltalk에서 보안

별도의 내용은 없습니다.


특별한 유형의 객체

Object 내 몇몇 메서드는 특별한 객체의 생성을 지원하는데, 이에 해당하는 객체는 다음과 같다:

  • 최종화 가능 (Finalizable) 객체
  • 약한 객체와 단명(ephemeron)객체 (예: 내용이 특별한 것으로 간주되는 객체, 쓰레기 수집의 힙(heap) 스캐닝 과정 중)
  • 읽기만 가능한 객체 (메서드에서 발견된 리터럴)
  • 고정 객체 (쓰레기 수집 간 이동이 불가능한)


위의 객체들은 다음과 같다:

makeWeak [Object 상의 메서드]

객체를 표시하여 다음 쓰레기 수집 pass에서 약한 것으로 간주되도록 하라. 쓰레기 수집기는 약한 객체 내에만 참조를 가진 객체를 사망한 것으로 간주하고, 이렇게 "거의 사망한(almost-dead)" 객체로의 참조는 nil로 대체한 후 객체로 애도(mourn) 메시지를 전송한다.


makeEphemeron [Object 상의 메서드]

객체를 표시하여 다음 쓰레기 수집 pass에서 특별한 것으로 간주되도록 하라. 단명 객체는 첫 번째 인스턴스 변수가 참조되지 않거나 단명 객체에서 다른 인스턴스 변수를 통해서만 참조되었을 때 애도 메시지가 전송된다.
Ephemeron은 쓰레기 수집기와 복잡한 상호작용을 프로그램화할 수 있는 매우 범용적 베이스를 제공한다 (예: 아래 설명된 최종화가 ephemeron으로 구현되는 경우).


addToBeFinalized [Object 상의 메서드]

객체를 표시하여 참조해제되는 즉시 그 finalize 메서드가 호출되도록 하라. finalize가 호출되기 전에 VM은 최종화 가능한 객체 목록에서 객체를 조용히 제거한다. 필요시 finalize 메서드는 객체를 다시 최종화 가능한 것으로 표시 가능하지만, 기본적으로 최종화는 한 번만 발생한다.
최종화 가능한 객체는 참조가 없을 때조차 메모리 안에 보관되는데, 교묘한 finalizer가 객체를 "소생(resuscitate)"시킬지도 모르기 때문이다; 최종화되지 않을 객체를 자동으로 표시하는 기능에는, 최종화가 발생한 이후에도 메모리를 낭비하도록 강요하는 대신 VM이 객체와 관련된 메모리의 해제(release)를 단순히 지연시킬 수 있는 훌륭한 부가적 효과가 있다.
이미지가 로딩될 때마다 최종화되어야 할 객체는 명시적으로 표시되어야 한다; 즉, 최종화 가능성은 이미지 저장으로 보존되지 않는다. 대부분의 경우 최종화는 이미지가 다시 로딩되면 오래되었을 운영 체제 자원과 함께 사용되기 때문에 발생한 일이다. 특히 CObjects의 경우 객체를 해제시키면 세그멘테이션 위반(segmentation violation)을 야기할 것이다.


removeToBeFinalized [Object 상의 메서드]

객체로부터 최종화 되어야 할 객체(to-be-finalized) 표시를 제거하라. 위에서 언급하였듯 객체에 대한 finalize 코드는 이를 명시적으로 실행할 필요가 없다.


finalize [Object 상의 메서드]

해당 메서드는 객체로의 참조가 더 이상 없는 (혹은 약한 객체 내부에서 참조만 가진 경우) VM에 의해 호출된다.


isReadOnly [Object 상의 메서드]

메서드가 become:, basicAt:put:, 혹은 at:put: 인 경우 (메서드 구현에 따라 다르다) VM이 객체에 대한 변경내용을 거절할 것인지 응답하는 메서드다. GNU Smalltalk는 고정된 인스턴스 변수로의 할당(assignment)을 가로막거나 instVarAt:put: 을 통한 할당을 가로막는 시도는 하지 않을 것이다. 많은 객체들이 (문자, nil, true, false, 메서드 리터럴) 기본적으로 읽기만 가능하다.


makeReadOnly: aBoolean [Object 상의 메서드]

수신자의 읽기만 가능 또는 읽기-쓰기 가능 상태를 aBoolean이 표시한 상태로 변경한다.


basicNewInFixedSpace [Object 상의 메서드]

#basicNew 와 동일하지만 객체가 쓰레기 수집 간 이동하지 않을 것이다.


basicNewInFixedSpace: [Object 상의 메서드]

#basicNew: 와 동일하지만 객체가 쓰레기 수집 간 이동하지 않을 것이다.


makeFixed [Object 상의 메서드]

수신자가 쓰레기 수집 간 이동하지 않도록 보장한다. 객체의 고정 시기를 객체의 생성 이후로 정하거나, 클래스가 객체의 생성에 #new 또는 #new: 의 사용을 지원하지 않는 경우에 모두 사용할 수 있다.


특정 애플리케이션은 고정되거나, 읽기만 가능하거나, 최종화 가능한 객체를 필요로 하겠지만 #makeWeak primitive를 필요로 하는 경우는 드물고, 약한 객체는 약한 컬렉션이라 불리는 것을 통해 간접적으로만 사용되는 것이 보통이다. 이는 추가 기능을 제공하기 때문에 사용이 훨씬 쉽다 (예: WeakArray는 항목이 쓰레기로 수집되었는지 결정할 수 있고, WeakSet는 해시 테이블 기능을 구현한다); 그 추가 기능이란 다음과 같다:

  • WeakArray
  • WeakSet
  • WeakKeyDictionary
  • WeakValueLookupTable
  • WeakIdentitySet
  • WeakKeyIdentityDictionary
  • WeakValueIdentityDictionary


GNU Smalltalk 2.1 이전 버전에 포함되었던 WeakKeyLookupTable 클래스는 WeakKeyDictionary로 대체되었다; 사용법은 완전히 동일하나 단명 객체를 기반으로 하여 더 효율적인 접근법을 사용하도록 그 구현법이 변경되었다.


Notes

  1. 반면 리터럴 Emacs Lisp 문자열에서는 가령 '\\\\'으로 주어져야 한다.
  2. 회색은 삼색(tri-color) 마케팅의 어휘에서 유래된 용어로, 가능한 모든 쓰레기 수집 알고리즘의 추상화이다: 삼색 마케팅에서 회색 객체는 접근이 가능하거나 우리가 재회수에 관심이 없는 객체지만 접근 가능한 것으로 언급하는 객체를 표시하기 위해 스캔하지 않은 객체다.
  3. 검정색 객체는 접근 가능한 것으로 알려져 있거나 우리가 재회수에 관심이 없는 객체로, 다른 검정색 또는 회색 객체에 대해서만 참조를 가진 것으로 알려져 있는 객체다 (궁금한 이를 위해 언급하자면, 삼색 마케팅 알고리즘은 다음과 같다: 접근 가능한 것으로 알려지지 않은 객체는 흰색으로, 모든 객체가 검정색 또는 흰색이라면 흰색은 쓰레기에 속한다).
  4. Free 페이지는 세 개의 힙(heap)에서 공유하는데, 세 가지는 바로 OldSpace, FixedSpace, malloc 힙이다. 큰 객체가 해제되면 그 객체가 사용했던 메모리는 malloc 또는 OldSpace 할당에 의해 재사용된다.
  5. Ordinary Object Pointer(일반 객체 포인터)의 약자이다.