Skip to content

Commit

Permalink
Add support for NSAutoreleasePool.
Browse files Browse the repository at this point in the history
  • Loading branch information
maleadt committed Feb 28, 2024
1 parent bbc8095 commit eb57b93
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 9 deletions.
72 changes: 64 additions & 8 deletions src/foundation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const NSIntegerMax = typemax(NSInteger)
const NSUIntegerMax = typemax(NSUInteger)


export NSObject, retain, release, is_kind_of
export NSObject, retain, release, autorelease, is_kind_of

@objcwrapper NSObject <: Object

Expand All @@ -41,6 +41,8 @@ end

release(obj::NSObject) = @objc [obj::id{NSObject} release]::Cvoid

autorelease(obj::NSObject) = @objc [obj::id{NSObject} autorelease]::Cvoid

retain(obj::NSObject) = @objc [obj::id{NSObject} retain]::Cvoid

ObjectiveC.class(obj::NSObject) = @objc [obj::id{NSObject} class]::Class
Expand Down Expand Up @@ -83,7 +85,8 @@ Base.:(==)(v1::NSValue, v2::NSValue) =
end

NSValue(x::Ptr) = NSValue(@objc [NSValue valueWithPointer:x::Ptr{Cvoid}]::id{NSValue})
NSValue(x::Union{NSRange,UnitRange}) = NSValue(@objc [NSValue valueWithRange:x::NSRange]::id{NSValue})
NSValue(x::Union{NSRange,UnitRange}) =
NSValue(@objc [NSValue valueWithRange:x::NSRange]::id{NSValue})
# ...


Expand Down Expand Up @@ -150,23 +153,29 @@ end
Base.cconvert(::Type{id{NSString}}, str::String) = NSString(str)
Base.convert(::Type{NSString}, str::String) = NSString(str)

Base.:(==)(s1::Union{String,NSString}, s2::Union{String,NSString}) = String(s1) == String(s2)
Base.:(==)(s1::NSString, s2::NSString) = @objc [s1::id{NSString} isEqualToString:s2::id{NSString}]::Bool
Base.:(==)(s1::Union{String,NSString}, s2::Union{String,NSString}) =
String(s1) == String(s2)
Base.:(==)(s1::NSString, s2::NSString) =
@objc [s1::id{NSString} isEqualToString:s2::id{NSString}]::Bool

NSString() = NSString(@objc [NSString string]::id{NSString})
NSString(data::String) = NSString(@objc [NSString stringWithUTF8String:data::Ptr{Cchar}]::id{NSString})
NSString(data::String) =
NSString(@objc [NSString stringWithUTF8String:data::Ptr{Cchar}]::id{NSString})
Base.length(s::NSString) = Int(s.length)
Base.String(s::NSString) = unsafe_string(@objc [s::id{NSString} UTF8String]::Ptr{Cchar})

# avoid redundant quotes
Base.string(s::NSString) = String(s)
Base.print(io::IO, s::NSString) = print(io, String(s))

Base.show(io::IO, ::MIME"text/plain", s::NSString) = print(io, "NSString(", repr(String(s)), ")")
Base.show(io::IO, ::MIME"text/plain", s::NSString) =

Check warning on line 171 in src/foundation.jl

View check run for this annotation

Codecov / codecov/patch

src/foundation.jl#L171

Added line #L171 was not covered by tests
print(io, "NSString(", repr(String(s)), ")")
Base.show(io::IO, s::NSString) = show(io, String(s))

Base.contains(s::NSString, t::AbstractString) = @objc [s::id{NSString} containsString:t::id{NSString}]::Bool
Base.contains(s::AbstractString, t::NSString) = @objc [s::id{NSString} containsString:t::id{NSString}]::Bool
Base.contains(s::NSString, t::AbstractString) =
@objc [s::id{NSString} containsString:t::id{NSString}]::Bool
Base.contains(s::AbstractString, t::NSString) =
@objc [s::id{NSString} containsString:t::id{NSString}]::Bool

Check warning on line 178 in src/foundation.jl

View check run for this annotation

Codecov / codecov/patch

src/foundation.jl#L177-L178

Added lines #L177 - L178 were not covered by tests


export NSArray
Expand Down Expand Up @@ -396,4 +405,51 @@ function Base.copy(block::NSBlock)
end


export NSAutoreleasePool, @autoreleasepool, drain

@objcwrapper immutable=false NSAutoreleasePool <: NSObject

function NSAutoreleasePool(; autorelease=true)
obj = NSAutoreleasePool(@objc [NSAutoreleasePool alloc]::id{NSAutoreleasePool})
if autorelease
finalizer(release, obj)

Check warning on line 415 in src/foundation.jl

View check run for this annotation

Codecov / codecov/patch

src/foundation.jl#L415

Added line #L415 was not covered by tests
end
# XXX: this init call itself requires an autoreleasepool to be active...
@objc [obj::id{NSAutoreleasePool} init]::id{NSAutoreleasePool}
obj
end

drain(pool::NSAutoreleasePool) = @objc [pool::id{NSAutoreleasePool} drain]::Cvoid

# high-level interface to wrap Julia code in an autorelease pool
const NSAutoreleaseLock = ReentrantLock()
function NSAutoreleasePool(f::Base.Callable)
# we cannot switch between multiple autorelease pools, so ensure only one is ever active.
# XXX: support multiple pools, as long as they run on separate threads?
Base.@lock NSAutoreleaseLock begin
# autorelease pools are thread-bound, so ensure we don't migrate to another thread
task = current_task()
sticky = task.sticky
task.sticky = true

# pools cannot be double released, so we need to disable the finalizer
pool = NSAutoreleasePool(; autorelease=false)
try
f()
finally
drain(pool)
task.sticky = sticky
end
end
end

# for compatibility with Objective-C code
macro autoreleasepool(ex)
quote
$NSAutoreleasePool() do
$(esc(ex))
end
end
end

end
28 changes: 27 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ using ObjectiveC
@test @objc [obj::id{Object} isEqualTo:obj::id{Object}]::Bool
empty_str = @objc [NSString string]::id{Object}
@objc [obj::id stringByReplacingOccurrencesOfString:empty_str::id{Object} withString:empty_str::id{Object}]::id{Object}

# chained class + install calls
@objc [[NSString alloc]::id{Object} init]::id{Object}
end

@objcwrapper TestNSString <: Object
Expand Down Expand Up @@ -101,9 +104,30 @@ end
end
end

using .Foundation
@testset "foundation" begin

using .Foundation
@testset "NSAutoReleasePool" begin
# MRC
obj = NSString(@objc [NSString new]::id{NSString})
@objc [obj::id{NSString} release]::id{NSString}

# low-level API
let pool=NSAutoreleasePool(; autorelease=false)
obj = NSString(@objc [NSString new]::id{NSString})
autorelease(obj)
drain(pool)
end

# high-level API
@autoreleasepool begin
# calling the `string` constructor means we don't need `autorelease`
@objc [NSString string]::id{NSString}
end
end

# run the remainder of the tests in an autorelease pool to avoid leaking objects
@autoreleasepool begin

@testset "NSString" begin
str = NSString()
Expand Down Expand Up @@ -225,6 +249,8 @@ end

end

end

@testset "dispatch" begin

using .Dispatch
Expand Down

0 comments on commit eb57b93

Please sign in to comment.