Stop requiring to inherit NSObject when passing Swift object to Kotlin

This commit is contained in:
SvyatoslavScherbina
2019-02-11 15:26:51 +03:00
committed by GitHub
parent 2d882d1b6c
commit 8d1ef86270
4 changed files with 62 additions and 7 deletions
-3
View File
@@ -239,9 +239,6 @@ val nsNumber = 42 as NSNumber
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes
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
@@ -145,6 +145,8 @@ interface I {
fun iFun(): String = "I::iFun"
}
fun I.iFunExt() = iFun()
private interface PI {
fun piFun(): Any
fun iFun(): String = "PI::iFun"
@@ -261,4 +263,6 @@ class Bridge : BridgeBase() {
override fun foo2() = throw MyException()
override fun foo3() = throw MyException()
override fun foo4() = throw MyException()
}
}
fun Any.same() = this
@@ -470,6 +470,26 @@ func testShared() throws {
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 --------
class ValuesTests : TestProvider {
@@ -505,6 +525,7 @@ class ValuesTests : TestProvider {
TestCase(name: "TestCompanionObj", method: withAutorelease(testCompanionObj)),
TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)),
TestCase(name: "TestShared", method: withAutorelease(testShared)),
TestCase(name: "TestPureSwiftClasses", method: withAutorelease(testPureSwiftClasses)),
]
}
}
+36 -3
View File
@@ -135,6 +135,8 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
@interface KotlinBase : NSObject <ConvertibleToKotlin, NSCopying>
@end;
static void initializeObjCExport();
@implementation KotlinBase {
KRefSharedHolder refHolder;
}
@@ -144,6 +146,9 @@ static inline id AtomicSetAssociatedObject(ObjHeader* obj, id associatedObject)
}
+(void)initialize {
if (self == [KotlinBase class]) {
initializeObjCExport();
}
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(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 initializeObjCExport() {
checkLoadedOnce();
SEL toKotlinSelector = @selector(toKotlin:);
Method toKotlinMethod = class_getClassMethod([NSObject class], toKotlinSelector);
RuntimeAssert(toKotlinMethod != nullptr, "");
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");
RuntimeAssert(nsBlockClass != nullptr, "NSBlock class not found");
@@ -399,6 +410,20 @@ static void initializeObjCExport() {
added = class_addMethod(booleanClass, toKotlinSelector, (IMP)boxedBooleanToKotlinImp, toKotlinTypeEncoding);
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>
@@ -416,11 +441,19 @@ static void initializeObjCExport() {
+(void)load {
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
initializeObjCExport();
checkLoadedOnce();
});
}
@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>
@end;