Stop requiring to inherit NSObject when passing Swift object to Kotlin
This commit is contained in:
committed by
GitHub
parent
2d882d1b6c
commit
8d1ef86270
@@ -239,9 +239,6 @@ val nsNumber = 42 as NSNumber
|
|||||||
|
|
||||||
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes
|
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes
|
||||||
and protocols.
|
and protocols.
|
||||||
Currently a class that adopts the Kotlin protocol should inherit `NSObject`
|
|
||||||
(either directly or indirectly). Note that all Kotlin classes do inherit `NSObject`,
|
|
||||||
so a Swift/Objective-C subclass of Kotlin class can adopt the Kotlin protocol.
|
|
||||||
|
|
||||||
### Subclassing Swift/Objective-C classes and protocols from Kotlin
|
### Subclassing Swift/Objective-C classes and protocols from Kotlin
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ interface I {
|
|||||||
fun iFun(): String = "I::iFun"
|
fun iFun(): String = "I::iFun"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun I.iFunExt() = iFun()
|
||||||
|
|
||||||
private interface PI {
|
private interface PI {
|
||||||
fun piFun(): Any
|
fun piFun(): Any
|
||||||
fun iFun(): String = "PI::iFun"
|
fun iFun(): String = "PI::iFun"
|
||||||
@@ -261,4 +263,6 @@ class Bridge : BridgeBase() {
|
|||||||
override fun foo2() = throw MyException()
|
override fun foo2() = throw MyException()
|
||||||
override fun foo3() = throw MyException()
|
override fun foo3() = throw MyException()
|
||||||
override fun foo4() = throw MyException()
|
override fun foo4() = throw MyException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Any.same() = this
|
||||||
|
|||||||
@@ -470,6 +470,26 @@ func testShared() throws {
|
|||||||
try assertNotFrozen(FinalClassExtOpen())
|
try assertNotFrozen(FinalClassExtOpen())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PureSwiftClass {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PureSwiftStruct {
|
||||||
|
var x: Int
|
||||||
|
}
|
||||||
|
class PureSwiftKotlinInterfaceImpl : I {
|
||||||
|
func iFun() -> String {
|
||||||
|
return "pure"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPureSwiftClasses() throws {
|
||||||
|
let pureSwiftClass = PureSwiftClass()
|
||||||
|
try assertTrue(ValuesKt.same(pureSwiftClass) as? AnyObject === pureSwiftClass)
|
||||||
|
|
||||||
|
try assertEquals(actual: 123, expected: (ValuesKt.same(PureSwiftStruct(x: 123)) as? PureSwiftStruct)?.x)
|
||||||
|
try assertEquals(actual: "pure", expected: ValuesKt.iFunExt(PureSwiftKotlinInterfaceImpl()))
|
||||||
|
}
|
||||||
|
|
||||||
// -------- Execution of the test --------
|
// -------- Execution of the test --------
|
||||||
|
|
||||||
class ValuesTests : TestProvider {
|
class ValuesTests : TestProvider {
|
||||||
@@ -505,6 +525,7 @@ class ValuesTests : TestProvider {
|
|||||||
TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)),
|
TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)),
|
||||||
TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)),
|
TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)),
|
||||||
TestCase(name: "TestShared", method: withAutorelease(testShared)),
|
TestCase(name: "TestShared", method: withAutorelease(testShared)),
|
||||||
|
TestCase(name: "TestPureSwiftClasses", method: withAutorelease(testPureSwiftClasses)),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
|
|||||||
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
|
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
|
static void initializeObjCExport();
|
||||||
|
|
||||||
@implementation KotlinBase {
|
@implementation KotlinBase {
|
||||||
KRefSharedHolder refHolder;
|
KRefSharedHolder refHolder;
|
||||||
}
|
}
|
||||||
@@ -144,6 +146,9 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
|
|||||||
}
|
}
|
||||||
|
|
||||||
+(void)initialize {
|
+(void)initialize {
|
||||||
|
if (self == [KotlinBase class]) {
|
||||||
|
initializeObjCExport();
|
||||||
|
}
|
||||||
initializeClass(self);
|
initializeClass(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,16 +381,22 @@ static ALWAYS_INLINE OBJ_GETTER(convertUnmappedObjCObject, id obj) {
|
|||||||
static OBJ_GETTER(blockToKotlinImp, id self, SEL cmd);
|
static OBJ_GETTER(blockToKotlinImp, id self, SEL cmd);
|
||||||
static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd);
|
static OBJ_GETTER(boxedBooleanToKotlinImp, NSNumber* self, SEL cmd);
|
||||||
|
|
||||||
|
static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd);
|
||||||
|
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd);
|
||||||
|
|
||||||
static void checkLoadedOnce();
|
static void checkLoadedOnce();
|
||||||
|
|
||||||
static void initializeObjCExport() {
|
static void initializeObjCExport() {
|
||||||
checkLoadedOnce();
|
|
||||||
|
|
||||||
SEL toKotlinSelector = @selector(toKotlin:);
|
SEL toKotlinSelector = @selector(toKotlin:);
|
||||||
Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector);
|
Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector);
|
||||||
RuntimeAssert(toKotlinMethod != nullptr, "");
|
RuntimeAssert(toKotlinMethod != nullptr, "");
|
||||||
const char* toKotlinTypeEncoding = method_getTypeEncoding(toKotlinMethod);
|
const char* toKotlinTypeEncoding = method_getTypeEncoding(toKotlinMethod);
|
||||||
|
|
||||||
|
SEL releaseAsAssociatedObjectSelector = @selector(releaseAsAssociatedObject);
|
||||||
|
Method releaseAsAssociatedObjectMethod = class_getClassMethod([NSObject class], releaseAsAssociatedObjectSelector);
|
||||||
|
RuntimeAssert(releaseAsAssociatedObjectMethod != nullptr, "");
|
||||||
|
const char* releaseAsAssociatedObjectTypeEncoding = method_getTypeEncoding(releaseAsAssociatedObjectMethod);
|
||||||
|
|
||||||
Class nsBlockClass = objc_getClass("NSBlock");
|
Class nsBlockClass = objc_getClass("NSBlock");
|
||||||
RuntimeAssert(nsBlockClass != nullptr, "NSBlock class not found");
|
RuntimeAssert(nsBlockClass != nullptr, "NSBlock class not found");
|
||||||
|
|
||||||
@@ -399,6 +410,20 @@ static void initializeObjCExport() {
|
|||||||
|
|
||||||
added = class_addMethod(booleanClass, toKotlinSelector, (IMP)boxedBooleanToKotlinImp, toKotlinTypeEncoding);
|
added = class_addMethod(booleanClass, toKotlinSelector, (IMP)boxedBooleanToKotlinImp, toKotlinTypeEncoding);
|
||||||
RuntimeAssert(added, "Unable to add 'toKotlin:' method to __NSCFBoolean class");
|
RuntimeAssert(added, "Unable to add 'toKotlin:' method to __NSCFBoolean class");
|
||||||
|
|
||||||
|
for (const char* swiftRootClassName : { "SwiftObject", "_TtCs12_SwiftObject" }) {
|
||||||
|
Class swiftRootClass = objc_getClass(swiftRootClassName);
|
||||||
|
if (swiftRootClass != nullptr) {
|
||||||
|
added = class_addMethod(swiftRootClass, toKotlinSelector, (IMP)SwiftObject_toKotlinImp, toKotlinTypeEncoding);
|
||||||
|
RuntimeAssert(added, "Unable to add 'toKotlin:' method to SwiftObject class");
|
||||||
|
|
||||||
|
added = class_addMethod(
|
||||||
|
swiftRootClass, releaseAsAssociatedObjectSelector,
|
||||||
|
(IMP)SwiftObject_releaseAsAssociatedObjectImp, releaseAsAssociatedObjectTypeEncoding
|
||||||
|
);
|
||||||
|
RuntimeAssert(added, "Unable to add 'releaseAsAssociatedObject' method to SwiftObject class");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
|
@interface NSObject (NSObjectToKotlin) <ConvertibleToKotlin>
|
||||||
@@ -416,11 +441,19 @@ static void initializeObjCExport() {
|
|||||||
+(void)load {
|
+(void)load {
|
||||||
static dispatch_once_t onceToken = 0;
|
static dispatch_once_t onceToken = 0;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
initializeObjCExport();
|
checkLoadedOnce();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
|
static OBJ_GETTER(SwiftObject_toKotlinImp, id self, SEL cmd) {
|
||||||
|
RETURN_RESULT_OF(convertUnmappedObjCObject, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SwiftObject_releaseAsAssociatedObjectImp(id self, SEL cmd) {
|
||||||
|
objc_release(self);
|
||||||
|
}
|
||||||
|
|
||||||
@interface NSString (NSStringToKotlin) <ConvertibleToKotlin>
|
@interface NSString (NSStringToKotlin) <ConvertibleToKotlin>
|
||||||
@end;
|
@end;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user