1 | #pragma once |
2 | |
3 | #include <string> |
4 | |
5 | #include "taichi/common/core.h" |
6 | |
7 | #ifdef TI_PLATFORM_OSX |
8 | |
9 | #include <objc/message.h> |
10 | #include <objc/objc.h> |
11 | #include <objc/runtime.h> |
12 | |
13 | namespace taichi { |
14 | namespace mac { |
15 | |
16 | template <typename R, typename O, typename... Args> |
17 | R cast_call(O *i, const char *select, Args... args) { |
18 | using func = R (*)(id, SEL, Args...); |
19 | return ((func)(objc_msgSend))(reinterpret_cast<id>(i), sel_getUid(select), |
20 | args...); |
21 | } |
22 | |
23 | template <typename O, typename... Args> |
24 | id call(O *i, const char *select, Args... args) { |
25 | return cast_call<id>(i, select, args...); |
26 | } |
27 | |
28 | template <typename R = id, typename... Args> |
29 | R clscall(const char *class_name, const char *select, Args... args) { |
30 | using func = R (*)(id, SEL, Args...); |
31 | return ((func)(objc_msgSend))((id)objc_getClass(class_name), |
32 | sel_getUid(select), args...); |
33 | } |
34 | |
35 | template <typename O> |
36 | class NsObjDeleter { |
37 | public: |
38 | void operator()(O *o) { |
39 | call(o, "release" ); |
40 | } |
41 | }; |
42 | |
43 | template <typename O> |
44 | using nsobj_unique_ptr = std::unique_ptr<O, NsObjDeleter<O>>; |
45 | |
46 | template <typename O> |
47 | nsobj_unique_ptr<O> wrap_as_nsobj_unique_ptr(O *nsobj) { |
48 | return nsobj_unique_ptr<O>(nsobj); |
49 | } |
50 | |
51 | template <typename O> |
52 | nsobj_unique_ptr<O> retain_and_wrap_as_nsobj_unique_ptr(O *nsobj) { |
53 | // On creating an object, it could be either the caller or the callee's |
54 | // responsibility to take the ownership of the object. By convention, method |
55 | // names with "alloc", "new", "create" imply that the caller owns the object. |
56 | // Otherwise, the object is tracked by an autoreleasepool before the callee |
57 | // returns it. |
58 | // |
59 | // For an object that is owned by the callee (released by autoreleasepool), if |
60 | // we want to *own* a reference to it, we must call [retain] to increment the |
61 | // reference counting. |
62 | // |
63 | // In practice, we find that each pthread (non main-thread) creates its own |
64 | // autoreleasepool. Without retaining the object, it has caused double-free |
65 | // on thread exit: |
66 | // 1. nsobj_unique_ptr calls [release] in its destructor. |
67 | // 2. autoreleasepool releases all the tracked objects upon thread exit. |
68 | // |
69 | // * https://stackoverflow.com/a/51080781/12003165 |
70 | // * |
71 | // https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW1 |
72 | call(nsobj, "retain" ); |
73 | return wrap_as_nsobj_unique_ptr(nsobj); |
74 | } |
75 | |
76 | // Prepend "TI_" to native ObjC type names, otherwise clang-format thinks this |
77 | // is an ObjC file and is not happy formatting it. |
78 | struct TI_NSString; |
79 | struct TI_NSArray; |
80 | |
81 | struct TI_NSRange { |
82 | size_t location{0}; |
83 | size_t length{0}; |
84 | }; |
85 | |
86 | // |str| must exist during the entire lifetime of the returned object, as it |
87 | // does not own the underlying memory. Think of it as std::string_view. |
88 | nsobj_unique_ptr<TI_NSString> wrap_string_as_ns_string(const std::string &str); |
89 | |
90 | std::string to_string(TI_NSString *ns); |
91 | |
92 | int ns_array_count(TI_NSArray *na); |
93 | |
94 | template <typename R> |
95 | R ns_array_object_at_index(TI_NSArray *na, int i) { |
96 | return cast_call<R>(na, "objectAtIndex:" , i); |
97 | } |
98 | |
99 | struct TI_NSAutoreleasePool; |
100 | |
101 | TI_NSAutoreleasePool *create_autorelease_pool(); |
102 | |
103 | void drain_autorelease_pool(TI_NSAutoreleasePool *pool); |
104 | |
105 | class ScopedAutoreleasePool { |
106 | public: |
107 | ScopedAutoreleasePool(); |
108 | ~ScopedAutoreleasePool(); |
109 | |
110 | private: |
111 | TI_NSAutoreleasePool *pool_; |
112 | }; |
113 | |
114 | } // namespace mac |
115 | } // namespace taichi |
116 | |
117 | #endif // TI_PLATFORM_OSX |
118 | |