og-corefoundation 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d9c0bb932c1b546f4408eac8bd38f9b8060897ca344655798954c099fd4a0fef
4
+ data.tar.gz: 6418e8243a77161eb8532911f36c936fcbc91bf3b70dd167dd2c536549b30a2f
5
+ SHA512:
6
+ metadata.gz: 9a0f068538b1ee65adf7afbd90e29548ca0f39711788f3e327ee8f2d35837da91a62f8f57111a55fc3ae56388d52e91d6cf12bce25df0bd0d16e9eaf6f79abae
7
+ data.tar.gz: 8ebd7c1d1c021e250dfb26fdded616db22ffb16ae5831232d244fdbfb222d92e8eeb284052fe33c4ffa4b73701afe04ed75190e01156b7f293fd3ab7452c50e6
data/CHANGELOG ADDED
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+
4
+ ## 0.2.1
5
+
6
+ fix finalizer exception
7
+
8
+ ## 0.2.0 - 2012-12-06
9
+ Add support for ruby 1.8.7 [tmaher]
10
+
11
+
12
+ ## 0.1.4 - 2012-10-16
13
+
14
+ Initial version
data/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ The MIT License
2
+ Copyright (c) 2012 Frederick Cheung
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ [![Build Status](https://travis-ci.org/fcheung/corefoundation.png)](https://travis-ci.org/fcheung/corefoundation)
2
+
3
+ CoreFoundation
4
+ ==============
5
+
6
+ FFI based wrappers for a subset of core foundation: various bits of CFString, CFData, CFArray, CFDictionary are available. Not that useful on its own but a useful building block for writing ffi wrappers of other OS X libraries.
7
+
8
+ Although the CF collection classes can store arbitrary pointer sized values this wrapper only supports storing CFTypes.
9
+
10
+ The CF namespace has the raw FFI generated method calls but it's usually easier to use the wrapper classes: `CF::String`, `CF::Date`, `CF::Array`, `CF::Dictionary`, `CF::Boolean` which try to present a rubyish view of the world (for example `CF::Array` implements `Enumerable`)
11
+
12
+ These implement methods for creating new instances from ruby objects (eg `CF::String.from_string("hello world")`) but you can also pass build them from an `FFI::Pointer`).
13
+
14
+ Converting
15
+ ===========
16
+
17
+ `CF::Base` objects has a `to_ruby` that creates a ruby object of the most approprite type (`String` for `CF::String`, `Time` for `CF::Date`, `Integer` or `Float` for `CF::Number` etc). The collection classes call `to_ruby` on their contents too.
18
+
19
+ In addition to the methods on the wrapper classes themselves, the ruby classes are extended with a `to_cf` method. Because CoreFoundation strings aren't arbitrary collections of bytes, `String#to_cf` will return a `CF::Data` if the string has the ASCII-8BIT encoding and a `CF::String` if not.
20
+
21
+ If you have an `FFI::Pointer` or a raw address then you can create a wrapper by passing it to `new`, for example `CF::String.new(some_pointer)`. This does *not* check that the pointer is actually a `CFString`. You can use `CF::Base.typecast` to construct an instance of the appropriate subclass, for example `CF::Base.typecast(some_pointer)` would return a `CF::String` if `some_pointer` was in fact a `CFStringRef`.
22
+
23
+ Memory Management
24
+ =================
25
+
26
+ The convenience methods for creating CF objects will release the cf object when they are garbage collected. Methods on the convenience classes will usually retain the result and mark it for releasing when they are garbage collected (for example `CF::Dictionary#[]` retains the returned value). You don't need to do any extra memory management on these.
27
+
28
+ If you pass an `FFI::Pointer` to `new` or `typecast` no assumptions are made for you. You should call `retain` or `release` to manage it manually. As an alternative to calling `release` manually, the `release_on_gc` method adds a finalizer to the wrapper that will call `CFRelease` on the Core Foundation object when the wrapper is garbage collected. You will almost certainly crash your ruby interpreter if you overrelease an object and you will leak memory if you overretain one.
29
+
30
+ If you use the raw api (eg `CF.CFArrayCreate`) then you're on your own.
31
+
32
+
33
+ Compatibility
34
+ =============
35
+
36
+ Should work in MRI 1.8.7 and above and jruby. Not compatible with rubinius due to rubinius' ffi implementation not supporting certain features. On 1.8.7 the `binary?` and `binary!` methods tag a string's binary-ness with a flag, on 1.9 these methods are just shortcuts for testing whether the encoding is Encoding::ASCII_8BIT
37
+
38
+ License
39
+ =======
40
+
41
+ Released under the MIT license. See LICENSE
@@ -0,0 +1,123 @@
1
+ module CF
2
+ typedef :pointer, :cfarrayref
3
+
4
+ # @private
5
+ class ArrayCallbacks < FFI::Struct
6
+ layout :version, :cfindex, #cfindex
7
+ :retain, :pointer,
8
+ :release, :pointer,
9
+ :copyDescription, :pointer,
10
+ :equal, :pointer
11
+ end
12
+
13
+ attach_variable :kCFTypeArrayCallBacks, ArrayCallbacks
14
+ attach_function :CFArrayCreate, [:pointer, :pointer, :cfindex, :pointer], :cfarrayref
15
+ attach_function :CFArrayCreateMutable, [:pointer, :cfindex, :pointer], :cfarrayref
16
+ attach_function :CFArrayGetValueAtIndex, [:pointer, :cfindex], :pointer
17
+ attach_function :CFArraySetValueAtIndex, [:pointer, :cfindex, :pointer], :void
18
+ attach_function :CFArrayAppendValue, [:pointer, :pointer], :void
19
+ attach_function :CFArrayGetCount, [:pointer], :cfindex
20
+
21
+ callback :each_applier, [:pointer, :pointer], :void
22
+
23
+ attach_function :CFArrayApplyFunction, [:cfarrayref, CF::Range.by_value, :each_applier, :pointer], :void
24
+
25
+
26
+ # Wrapper class for CFArrayRef. It implements enumberable so you can use a lot of your favourite ruby methods on it.
27
+ #
28
+ # Values returned by the accessor methods or yielded by the block are retained and marked as releasable on garbage collection
29
+ # This means you can safely use the returned values even if the CFArray itself has been destroyed
30
+ #
31
+ # Unlike ruby arrays you cannot set arbitary array indexes - You can only set indexes in the range 0..length
32
+ class Array < Base
33
+ include Enumerable
34
+ register_type("CFArray")
35
+
36
+ # Whether the array is mutable
37
+ #
38
+ # WARNING: this only works for arrays created by CF::Array, there is no public api for telling whether an arbitrary
39
+ # CFTypeRef is a mutable array or not
40
+ def mutable?
41
+ @mutable
42
+ end
43
+
44
+ # Iterates over the array yielding the value to the block
45
+ # The value is wrapped in the appropriate CF::Base subclass and retained (but marked for releasing upon garbage collection)
46
+ # @return self
47
+ def each
48
+ range = CF::Range.new
49
+ range[:location] = 0
50
+ range[:length] = length
51
+ callback = lambda do |value, _|
52
+ yield Base.typecast(value).retain.release_on_gc
53
+ end
54
+ CF.CFArrayApplyFunction(self, range, callback, nil)
55
+ self
56
+ end
57
+
58
+ # Creates a new, immutable CFArray from a ruby array of cf objects
59
+ # @param [Array<CF::Base>] array The objects to place in the array. They must inherit from CF::Base
60
+ # @return [CF::Array] A CF::Array containing the objects, setup to release the array upon garbage collection
61
+ def self.immutable(array)
62
+ if bad_element = array.detect {|value| !value.is_a?(CF::Base)}
63
+ raise TypeError, "Array contains non cftype #{bad_element.inspect}"
64
+ end
65
+ m = FFI::MemoryPointer.new(:pointer, array.length)
66
+ m.write_array_of_pointer(array)
67
+ new(CF.CFArrayCreate(nil,m,array.length,CF::kCFTypeArrayCallBacks.to_ptr)).release_on_gc
68
+ end
69
+
70
+ # Creates a new, empty mutable CFArray
71
+ # @return [CF::Array] A mutable CF::Array containing the objects, setup to release the array upon garbage collection
72
+ def self.mutable
73
+ result = new(CF.CFArrayCreateMutable nil, 0, CF::kCFTypeArrayCallBacks.to_ptr).release_on_gc
74
+ result.instance_variable_set(:@mutable, true)
75
+ result
76
+ end
77
+
78
+ # Returns the object at the index
79
+ # @param [Integer] index the 0 based index of the item to retrieve. Behaviour is undefined if it is not in the range 0...size
80
+ # @return [CF::Base] a subclass of CF::Base
81
+ def [](index)
82
+ Base.typecast(CF.CFArrayGetValueAtIndex(self, index)).retain.release_on_gc
83
+ end
84
+
85
+ # Sets object at the index
86
+ # @param [Integer] index the 0 based index of the item to retrieve. Behaviour is undefined if it is not in the range 0..size
87
+ # It is legal to set the value at index n of a n item array - this is equivalent to appending the object
88
+ #
89
+ # @param [CF::Base] value the value to store
90
+ # @return [CF::Base] the store value
91
+ def []=(index, value)
92
+ raise TypeError, "instance is not mutable" unless mutable?
93
+ self.class.check_cftype(value)
94
+ CF.CFArraySetValueAtIndex(self, index, value)
95
+ value
96
+ end
97
+
98
+ # Appends a value to the array
99
+ #
100
+ # @return [CF::Array] self
101
+ def <<(value)
102
+ raise TypeError, "instance is not mutable" unless mutable?
103
+ self.class.check_cftype(value)
104
+ CF.CFArrayAppendValue(self, value)
105
+ self
106
+ end
107
+
108
+ # Returns a ruby array containing the result of calling to_ruby on each of the array's elements
109
+ #
110
+ def to_ruby
111
+ collect(&:to_ruby)
112
+ end
113
+
114
+ alias_method :push, :<<
115
+
116
+ # Returns the number of elements the array contains
117
+ # @return [Integer]
118
+ def length
119
+ CF.CFArrayGetCount(self)
120
+ end
121
+ alias_method :size, :length
122
+ end
123
+ end
@@ -0,0 +1,197 @@
1
+
2
+ # The top level namespace for the corefoundation library. The raw FFI generated methods are attached here
3
+ #
4
+ #
5
+ module CF
6
+ extend FFI::Library
7
+ ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation'
8
+
9
+ if FFI::Platform::ARCH == 'x86_64'
10
+ typedef :long_long, :cfindex
11
+ typedef :long_long, :cfcomparisonresult
12
+ typedef :ulong_long, :cfoptionflags
13
+ typedef :ulong_long, :cftypeid
14
+ typedef :ulong_long, :cfhashcode
15
+ else
16
+ typedef :long, :cfindex
17
+ typedef :long, :cfcomparisonresult
18
+ typedef :ulong, :cfoptionflags
19
+ typedef :ulong, :cftypeid
20
+ typedef :ulong, :cfhashcode
21
+ end
22
+
23
+
24
+ # @private
25
+ class Range < FFI::Struct
26
+ layout :location, :cfindex,
27
+ :length, :cfindex
28
+ end
29
+
30
+ typedef :pointer, :cftyperef
31
+
32
+ #general utility functions
33
+ attach_function :show, 'CFShow', [:cftyperef], :void
34
+ attach_function :release, 'CFRelease', [:cftyperef], :void
35
+ attach_function :retain, 'CFRetain', [:cftyperef], :cftyperef
36
+ attach_function 'CFEqual', [:cftyperef, :cftyperef], :char
37
+ attach_function 'CFHash', [:cftyperef], :cfhashcode
38
+ attach_function 'CFCopyDescription', [:cftyperef], :cftyperef
39
+ attach_function 'CFGetTypeID', [:cftyperef], :cftypeid
40
+
41
+ # The base class for all of the wrapper classes
42
+ #
43
+ # @abstract
44
+ class Base
45
+ @@type_map = {}
46
+
47
+ # @private
48
+ class Releaser
49
+ def initialize(ptr)
50
+ @address = ptr.address
51
+ end
52
+
53
+ def call *ignored
54
+ if @address != 0
55
+ CF.release(FFI::Pointer.new(@address))
56
+ @address = 0
57
+ end
58
+ end
59
+ end
60
+
61
+ class << self
62
+ # Raises an exception if the argument does not inherit from CF::Base
63
+ #
64
+ # @param cftyperef object to check
65
+ def check_cftype(cftyperef)
66
+ raise TypeError, "#{cftyperef.inspect} is not a cftype" unless cftyperef.is_a?(CF::Base)
67
+ end
68
+
69
+ # Wraps an FFI::Pointer with the appropriate subclass of CF::Base
70
+ # If the pointer is not a CFTypeRef behaviour is undefined
71
+ # @param [FFI::Pointer] cftyperef Object to wrap
72
+ # @return A wrapper object inheriting from CF::Base
73
+ def typecast(cftyperef)
74
+ klass = klass_from_cf_type cftyperef
75
+ klass.new(cftyperef)
76
+ end
77
+
78
+ private
79
+ def klass_from_cf_type cftyperef
80
+ klass = @@type_map[CF.CFGetTypeID(cftyperef)]
81
+ if !klass
82
+ raise TypeError, "No class registered for cf type #{cftyperef.inspect}"
83
+ end
84
+ klass
85
+ end
86
+
87
+ def register_type(type_name)
88
+ CF.attach_function "#{type_name}GetTypeID", [], :cftypeid
89
+ @@type_map[CF.send("#{type_name}GetTypeID")] = self
90
+ end
91
+
92
+ end
93
+
94
+ #
95
+ #
96
+ # @param [FFI::Pointer] ptr The pointer to wrap
97
+ def initialize(ptr)
98
+ @ptr = FFI::Pointer.new(ptr)
99
+ end
100
+
101
+ # Whether the instance is the CFNull singleton
102
+ #
103
+ # @return [Boolean]
104
+ def null?
105
+ equals?(CF::NULL)
106
+ end
107
+
108
+ # Returns the wrapped pointer
109
+ # @return [FFI::Pointer]
110
+ def to_ptr
111
+ @ptr
112
+ end
113
+
114
+ # Sets the wrapper pointer. You should only do this rarely. If you have used release_on_gc then that
115
+ # finalizer will still be called on the original pointer value
116
+ #
117
+ def ptr= ptr
118
+ @ptr = ptr
119
+ end
120
+
121
+ # Calls CFRetain on the wrapped pointer
122
+ # @return Returns self
123
+ def retain
124
+ CF.retain(self)
125
+ self
126
+ end
127
+
128
+ # Calls CFRelease on the wrapped pointer
129
+ # @return Returns self
130
+ def release
131
+ CF.release(self)
132
+ self
133
+ end
134
+
135
+ # Installs a finalizer on the wrapper object that will cause it to call CFRelease on the pointer
136
+ # when the wrapper is garbage collected
137
+ # @return Returns self
138
+ def release_on_gc
139
+ ObjectSpace.define_finalizer(@ptr, Releaser.new(@ptr))
140
+ self
141
+ end
142
+
143
+ # Returns a ruby string containing the output of CFCopyDescription for the wrapped object
144
+ #
145
+ # @return [String]
146
+ def inspect
147
+ cf = CF::String.new(CF.CFCopyDescription(self))
148
+ cf.to_s.tap {cf.release}
149
+ end
150
+
151
+ # Uses CFHash to return a hash code
152
+ #
153
+ # @return [Integer]
154
+ def hash
155
+ CF.CFHash(self)
156
+ end
157
+
158
+ # eql? (and ==) are implemented using CFEqual
159
+ #
160
+ #
161
+ def eql?(other)
162
+ if other.is_a?(CF::Base)
163
+ CF.CFEqual(self, other) != 0
164
+ else
165
+ false
166
+ end
167
+ end
168
+
169
+ # equals? is defined as returning true if the wrapped pointer is the same
170
+ #
171
+ #
172
+ def equals?(other)
173
+ if other.is_a?(CF::Base)
174
+ @ptr.address == other.to_ptr.address
175
+ else
176
+ false
177
+ end
178
+ end
179
+
180
+ alias_method :==, :eql?
181
+
182
+ # Converts the CF object into a ruby object. Subclasses typically override this to return
183
+ # an appropriate object (for example CF::String returns a String)
184
+ #
185
+ def to_ruby
186
+ self
187
+ end
188
+
189
+ # to_cf is a no-op on CF::Base and its descendants - it always returns self
190
+ #
191
+ #
192
+ def to_cf
193
+ self
194
+ end
195
+ end
196
+
197
+ end
@@ -0,0 +1,25 @@
1
+ module CF
2
+ attach_variable 'kCFBooleanTrue', :pointer
3
+ attach_variable 'kCFBooleanFalse', :pointer
4
+ attach_function 'CFBooleanGetValue', [:pointer], :uchar
5
+
6
+ # Wrapper for CFBooleanRef.
7
+ # Typically you use the CF::Boolean::TRUE and CF::Boolean::FALSE constants
8
+ #
9
+ class Boolean < Base
10
+ register_type("CFBoolean")
11
+ # A constant containing kCFBooleanTrue
12
+ TRUE = new(CF.kCFBooleanTrue)
13
+ # A constant containing kCFBooleanFalse
14
+ FALSE = new(CF.kCFBooleanFalse)
15
+
16
+ # returns a ruby true/false value
17
+ #
18
+ # @return [Boolean]
19
+ def value
20
+ CF.CFBooleanGetValue(self) != 0
21
+ end
22
+
23
+ alias_method :to_ruby, :value
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ module CF
2
+ typedef :pointer, :cfdataref
3
+
4
+ attach_function 'CFDataCreate', [:pointer, :buffer_in, :cfindex], :cfdataref
5
+ attach_function 'CFDataGetLength', [:cfdataref], :cfindex
6
+ attach_function 'CFDataGetBytePtr', [:cfdataref], :pointer
7
+
8
+ # Wrapper for CFData
9
+ #
10
+ #
11
+ class Data < Base
12
+ register_type("CFData")
13
+
14
+ # Creates a CFData from a ruby string
15
+ # @param [String] s the string to use
16
+ # @return [CF::Data]
17
+ def self.from_string(s)
18
+ new(CF.CFDataCreate(nil, s, s.bytesize)).release_on_gc
19
+ end
20
+
21
+ # Creates a ruby string from the wrapped data. The encoding will always be ASCII_8BIT
22
+ #
23
+ # @return [String]
24
+ def to_s
25
+ ptr = CF.CFDataGetBytePtr(self)
26
+ if CF::String::HAS_ENCODING
27
+ ptr.read_string(CF.CFDataGetLength(self)).force_encoding(Encoding::ASCII_8BIT)
28
+ else
29
+ ptr.read_string(CF.CFDataGetLength(self))
30
+ end
31
+ end
32
+
33
+ # The size in bytes of the CFData
34
+ # @return [Integer]
35
+ def size
36
+ CF.CFDataGetLength(self)
37
+ end
38
+
39
+ alias_method :to_ruby, :to_s
40
+ end
41
+ end
42
+
@@ -0,0 +1,32 @@
1
+ module CF
2
+ typedef :pointer, :cfdateref
3
+ typedef :double, :cfabsolutetime
4
+
5
+ attach_function :CFDateCreate, [:pointer, :cfabsolutetime], :cfdateref
6
+ attach_function :CFDateGetAbsoluteTime, [:cfdateref], :cfabsolutetime
7
+ attach_variable :kCFAbsoluteTimeIntervalSince1970, :double
8
+
9
+ # Wrapper for CFDateRef
10
+ #
11
+ #
12
+ class Date < Base
13
+ register_type 'CFDate'
14
+
15
+ # constructs a CF::Date from a ruby time
16
+ #
17
+ # @param [Time] time
18
+ # @return [CF::Date] a CF::Date instance that will be released on garbage collection
19
+ def self.from_time(time)
20
+ new(CF.CFDateCreate(nil, time.to_f - CF.kCFAbsoluteTimeIntervalSince1970)).release_on_gc
21
+ end
22
+
23
+ # returns a ruby Time instance corresponding to the same point in time
24
+ #
25
+ # @return [Time]
26
+ def to_time
27
+ Time.at(CF.CFDateGetAbsoluteTime(self) + CF.kCFAbsoluteTimeIntervalSince1970)
28
+ end
29
+
30
+ alias_method :to_ruby, :to_time
31
+ end
32
+ end
@@ -0,0 +1,114 @@
1
+ module CF
2
+ # @private
3
+ class DictionaryKeyCallbacks < FFI::Struct
4
+ layout :version, :cfindex,
5
+ :retain, :pointer,
6
+ :release, :pointer,
7
+ :copyDescription, :pointer,
8
+ :equal, :pointer,
9
+ :hash, :pointer
10
+ end
11
+ # @private
12
+ class DictionaryValueCallbacks < FFI::Struct
13
+ layout :version, :cfindex,
14
+ :retain, :pointer,
15
+ :release, :pointer,
16
+ :copyDescription, :pointer,
17
+ :equal, :pointer
18
+ end
19
+
20
+ typedef :pointer, :cfdictionaryref
21
+ attach_variable :kCFTypeDictionaryKeyCallBacks, DictionaryKeyCallbacks
22
+ attach_variable :kCFTypeDictionaryValueCallBacks, DictionaryValueCallbacks
23
+ attach_function :CFDictionaryCreateMutable, [:pointer, :cfindex, :pointer, :pointer], :cfdictionaryref
24
+
25
+ attach_function :CFDictionarySetValue, [:cfdictionaryref, :pointer, :pointer], :void
26
+ attach_function :CFDictionaryGetValue, [:cfdictionaryref, :pointer], :pointer
27
+
28
+ attach_function :CFDictionaryGetValue, [:cfdictionaryref, :pointer], :pointer
29
+ attach_function :CFDictionaryGetCount, [:cfdictionaryref], :cfindex
30
+
31
+ callback :each_applier, [:pointer, :pointer, :pointer], :void
32
+
33
+ attach_function :CFDictionaryApplyFunction, [:cfdictionaryref, :each_applier, :pointer], :void
34
+
35
+ # Wrapper class for CFDictionary. It implements Enumerable.
36
+ #
37
+ # Values returned by the accessor methods or yielded by the block are retained and marked as releasable on garbage collection
38
+ # This means you can safely use the returned values even if the CFDictionary itself has been destroyed
39
+ #
40
+ class Dictionary < Base
41
+ register_type("CFDictionary")
42
+ include Enumerable
43
+
44
+ # Return a new, empty mutable CF::Dictionary
45
+ #
46
+ #
47
+ # @return [CF::Dictionary]
48
+ def self.mutable
49
+ new(CF.CFDictionaryCreateMutable nil, 0, CF.kCFTypeDictionaryKeyCallBacks.to_ptr, CF.kCFTypeDictionaryValueCallBacks.to_ptr).release_on_gc
50
+ end
51
+
52
+ # Iterates over the array yielding the value to the block
53
+ # The value is wrapped in the appropriate CF::Base subclass and retained (but marked for releasing upon garbage collection)
54
+ # @return self
55
+
56
+ def each
57
+ callback = lambda do |key, value, _|
58
+ yield [Base.typecast(key).retain.release_on_gc, Base.typecast(value).retain.release_on_gc]
59
+ end
60
+ CF.CFDictionaryApplyFunction(self, callback, nil)
61
+ self
62
+ end
63
+
64
+ # Returns the value associated with the key, or nil
65
+ # The key should be a CF::Base instance. As a convenience, instances of string will be converted to CF::String
66
+ # @param [CF::Base, String] key the key to access
67
+ # @return [CF::Base, nil]
68
+
69
+ def [](key)
70
+ key = CF::String.from_string(key) if key.is_a?(::String)
71
+ self.class.check_cftype(key)
72
+ raw = CF.CFDictionaryGetValue(self, key)
73
+ raw.null? ? nil : self.class.typecast(raw).retain.release_on_gc
74
+ end
75
+
76
+ # Sets the value associated with the key, or nil
77
+ # The key should be a CF::Base instance. As a convenience, instances of string will be converted to CF::String
78
+ # @param [CF::Base, String] key the key to access
79
+ # @param [CF::Base] value the value to set
80
+ # @return [CF::Base] the value that was set
81
+ def []=(key, value)
82
+ key = CF::String.from_string(key) if key.is_a?(::String)
83
+ self.class.check_cftype(key)
84
+ self.class.check_cftype(value)
85
+ CF.CFDictionarySetValue(self, key, value)
86
+ value
87
+ end
88
+
89
+ # Adds the key value pairs from the argument to self
90
+ #
91
+ # @param [CF::Dictionary] other
92
+ # @return self
93
+ def merge!(other)
94
+ other.each do |k,v|
95
+ self[k] = v
96
+ end
97
+ self
98
+ end
99
+
100
+ # Returns the number of key value pairs in the dictionary
101
+ # @return [Integer]
102
+ def length
103
+ CF.CFDictionaryGetCount(self)
104
+ end
105
+
106
+ # Returns a ruby hash constructed by calling `to_ruby` on each key and value
107
+ #
108
+ # @return [Hash]
109
+ def to_ruby
110
+ Hash[collect {|k,v| [k.to_ruby, v.to_ruby]}]
111
+ end
112
+ alias_method :size, :length
113
+ end
114
+ end