iterator: support iteration using NSFastEnumeration
Implement the consumer side of the `NSFastEnumeration` protocol, matching the compilation scheme used by Clang for Objective‐C `for…in` loops.
This commit is contained in:
parent
1b736ace69
commit
e1c328f647
3 changed files with 121 additions and 0 deletions
115
src/iterator.zig
Normal file
115
src/iterator.zig
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const objc = @import("main.zig");
|
||||||
|
|
||||||
|
// From <Foundation/NSEnumerator.h>.
|
||||||
|
const NSFastEnumerationState = extern struct {
|
||||||
|
state: c_ulong,
|
||||||
|
itemsPtr: ?[*]objc.c.id,
|
||||||
|
mutationsPtr: ?*c_ulong,
|
||||||
|
extra: [5]c_ulong,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Iterator = struct {
|
||||||
|
object: objc.Object,
|
||||||
|
sel: objc.Sel,
|
||||||
|
state: NSFastEnumerationState,
|
||||||
|
initial_mutations_value: ?c_ulong,
|
||||||
|
// Clang compiles `for…in` loops with a size 16 buffer.
|
||||||
|
buffer: [16]objc.c.id,
|
||||||
|
slice: []objc.c.id,
|
||||||
|
|
||||||
|
pub fn init(object: objc.Object) Iterator {
|
||||||
|
return .{
|
||||||
|
.object = object,
|
||||||
|
.sel = objc.sel("countByEnumeratingWithState:objects:count:"),
|
||||||
|
.state = .{
|
||||||
|
.state = 0,
|
||||||
|
.itemsPtr = null,
|
||||||
|
.mutationsPtr = null,
|
||||||
|
.extra = [_]c_ulong{0} ** 5,
|
||||||
|
},
|
||||||
|
.initial_mutations_value = null,
|
||||||
|
.buffer = [_]objc.c.id{null} ** 16,
|
||||||
|
.slice = &[_]objc.c.id{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *Iterator) ?objc.Object {
|
||||||
|
if (self.slice.len == 0) {
|
||||||
|
// Ask for some more objects.
|
||||||
|
const count = self.object.msgSend(c_ulong, self.sel, .{
|
||||||
|
&self.state,
|
||||||
|
&self.buffer,
|
||||||
|
self.buffer.len,
|
||||||
|
});
|
||||||
|
if (self.initial_mutations_value) |value| {
|
||||||
|
// Call the mutation handler if the mutations value has
|
||||||
|
// changed since the start of iteration.
|
||||||
|
if (value != self.state.mutationsPtr.?.*) {
|
||||||
|
objc.c.objc_enumerationMutation(self.object.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.initial_mutations_value = self.state.mutationsPtr.?.*;
|
||||||
|
}
|
||||||
|
self.slice = self.state.itemsPtr.?[0..count];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.slice.len > 0) {
|
||||||
|
const first = self.slice[0];
|
||||||
|
self.slice = self.slice[1..];
|
||||||
|
return objc.Object.fromId(first);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "NSArray iteration" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const NSArray = objc.getClass("NSMutableArray").?;
|
||||||
|
const NSNumber = objc.getClass("NSNumber").?;
|
||||||
|
const array = NSArray.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
"arrayWithCapacity:",
|
||||||
|
.{@as(c_ulong, 10)},
|
||||||
|
);
|
||||||
|
defer array.release();
|
||||||
|
for (0..@as(c_int, 10)) |i| {
|
||||||
|
const i_number = NSNumber.msgSend(objc.Object, "numberWithInt:", .{i});
|
||||||
|
defer i_number.release();
|
||||||
|
array.msgSend(void, "addObject:", .{i_number});
|
||||||
|
}
|
||||||
|
var result: c_int = 0;
|
||||||
|
var iter = array.iterate();
|
||||||
|
while (iter.next()) |elem| {
|
||||||
|
result = (result * 10) + elem.getProperty(c_int, "intValue");
|
||||||
|
}
|
||||||
|
try testing.expectEqual(123456789, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "NSDictionary iteration" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const NSMutableDictionary = objc.getClass("NSMutableDictionary").?;
|
||||||
|
const NSNumber = objc.getClass("NSNumber").?;
|
||||||
|
const dict = NSMutableDictionary.msgSend(
|
||||||
|
objc.Object,
|
||||||
|
"dictionaryWithCapacity:",
|
||||||
|
.{@as(c_ulong, 100)},
|
||||||
|
);
|
||||||
|
defer dict.release();
|
||||||
|
for (0..@as(c_int, 100)) |i| {
|
||||||
|
const i_number = NSNumber.msgSend(objc.Object, "numberWithInt:", .{i});
|
||||||
|
defer i_number.release();
|
||||||
|
dict.msgSend(void, "setValue:forKey:", .{
|
||||||
|
i_number,
|
||||||
|
i_number.getProperty(objc.Object, "stringValue"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var result: c_int = 0;
|
||||||
|
var iter = dict.iterate();
|
||||||
|
while (iter.next()) |key| {
|
||||||
|
const value = dict.msgSend(objc.Object, "valueForKey:", .{key});
|
||||||
|
result += value.getProperty(c_int, "intValue");
|
||||||
|
}
|
||||||
|
try testing.expectEqual(4950, result);
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ pub usingnamespace @import("autorelease.zig");
|
||||||
pub usingnamespace @import("block.zig");
|
pub usingnamespace @import("block.zig");
|
||||||
pub usingnamespace @import("class.zig");
|
pub usingnamespace @import("class.zig");
|
||||||
pub usingnamespace @import("encoding.zig");
|
pub usingnamespace @import("encoding.zig");
|
||||||
|
pub usingnamespace @import("iterator.zig");
|
||||||
pub usingnamespace @import("object.zig");
|
pub usingnamespace @import("object.zig");
|
||||||
pub usingnamespace @import("property.zig");
|
pub usingnamespace @import("property.zig");
|
||||||
pub usingnamespace @import("protocol.zig");
|
pub usingnamespace @import("protocol.zig");
|
||||||
|
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
const objc = @import("main.zig");
|
const objc = @import("main.zig");
|
||||||
const MsgSend = @import("msg_send.zig").MsgSend;
|
const MsgSend = @import("msg_send.zig").MsgSend;
|
||||||
|
const Iterator = @import("iterator.zig").Iterator;
|
||||||
|
|
||||||
/// Object is an instance of a class.
|
/// Object is an instance of a class.
|
||||||
pub const Object = struct {
|
pub const Object = struct {
|
||||||
|
@ -104,6 +105,10 @@ pub const Object = struct {
|
||||||
pub fn release(self: Object) void {
|
pub fn release(self: Object) void {
|
||||||
objc_release(self.value);
|
objc_release(self.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iterate(self: Object) Iterator {
|
||||||
|
return Iterator.init(self);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "c" fn objc_retain(objc.c.id) objc.c.id;
|
extern "c" fn objc_retain(objc.c.id) objc.c.id;
|
||||||
|
|
Loading…
Add table
Reference in a new issue