Swift类是否具有可以重新映射的isa指针?
我们已经看到Swift 使用比Objective-C 更静态的方法调度,它(除非来自Foundation/NSObject的类设备)在运行时基于重映射方法实现防止了调配风格.
我想知道我们将如何实现基于方法拦截的动态功能,如观察者模式,通知等?目前所有这些东西都是由Objective-C层提供的,可以很容易地集成到Swift中.但是,如果我们想在我们自己的框架(或应用程序)中提供这些类型的功能,是否有必要在Objective-C中实现它们?我认为有一种方法可以"原生"地完成它.
另一种对Objective-C来说很常见的混合是重新映射isa-pointer来动态生成一个子类.Swift是否支持这种调配?如果没有什么是拦截任意的方法调用的支持呢?
编辑: 正如@jatoben指出的那样,从arm64开始重新映射必须通过调用object_setClass()而不是直接访问该值来完成.这仍然被称为'isa指针调配'
如果实例变量属于类的实例,那么类变量将属于元类的实例,我认为.但是我对Objective-C元类的经验告诉我,这不太可能.
我想知道class_getClassVariable相反的是什么class_getInstanceVariable,以及为什么class_setClassVariable运行时没有.
metaclass class objective-c objective-c-runtime class-variables
给定Objective-C类型type,可以轻松获得该类型的编码 encoding和大小size:
const char *encoding = @encode(type);
size_t size = sizeof(type);
Run Code Online (Sandbox Code Playgroud)
换句话说,我们有映射
@encode: type_t -> const char *
sizeof: type_t -> size_t
Run Code Online (Sandbox Code Playgroud)
这提出了两个问题:
(1)假设我们只有一个编码,而不是一个类型.获得映射会很不错
sizeofencodedtype: const char * -> size_t
Run Code Online (Sandbox Code Playgroud)
这样,对于每个type_t type我们都有
sizeofencodedtype(@encode(type)) = sizeof(type)
Run Code Online (Sandbox Code Playgroud)
这样的功能是否已经存在?如果没有,怎么可能建立一个?
(2)理想情况下,我们可以反转@encode映射来进行映射
decode: const char * -> type_t
Run Code Online (Sandbox Code Playgroud)
但这似乎不可能,因为type_t不是真正的C类型.我想我可以等待@decode添加到语言中,但这不是很现实或时间敏感.但是,如果不能使用其编码在运行时实例化类型吗?
如果我得到以下Objective-C源文件:
// test.m
#import <objc/Object.h>
@interface MySuperClass: Object {
}
-(void) myMessage1;
@end
@implementation MySuperClass
-(void) myMessage1 {
}
@end
@interface MyClass: MySuperClass {
}
-(void) myMessage2;
@end
@implementation MyClass
-(void) myMessage2 {
}
@end
int main() {
return 0;
}
Run Code Online (Sandbox Code Playgroud)
并尝试从中生成一个程序集文件clang -fobjc-nonfragile-abi -fnext-runtime -S test.m,我得到以下汇编代码:
.file "test.m"
.text
.align 16, 0x90
.type _2D__5B_MySuperClass_20_myMessage1_5D_,@function
_2D__5B_MySuperClass_20_myMessage1_5D_: # @"\01-[MySuperClass myMessage1]"
.Ltmp0:
.cfi_startproc
# BB#0:
movq %rdi, -8(%rsp)
movq %rsi, -16(%rsp)
ret
.Ltmp1:
.size _2D__5B_MySuperClass_20_myMessage1_5D_, .Ltmp1-_2D__5B_MySuperClass_20_myMessage1_5D_
.Ltmp2:
.cfi_endproc
.Leh_func_end0:
.align 16, …Run Code Online (Sandbox Code Playgroud) 查看Objective-C运行时库源代码,特别是在objc-runtime-new.mm,我看到了一些函数甚至注释引用了懒惰和非惰性类.似乎没有一个类+load的方法被称为懒类,但我不知道这一点,并最有可能是不正确的.在Google上搜索后,我没有在Objective-C上找到任何有关惰性类的内容.
那么,什么是Objective-C中的惰性类?Obj-C有这个功能吗?它是否与+load类实现中存在方法有关?在上面链接的文件中,运行时系统调用一个被调用的函数_getObjc2NonlazyClassList,以便从图像中获取非惰性类的列表.为什么没有_getObjc2LazyClassList功能呢?
以下是获取元类的正确方法?
Class myMetaClass = objc_getMetaClass("NSString");
Run Code Online (Sandbox Code Playgroud)
要么:
Class myMetaClass = object_getClass([NSString class]);
Run Code Online (Sandbox Code Playgroud)
它们有什么不同吗?
正如第一个回答者在这里链接的另一篇文章所述:
请告诉我为什么(????) objc_getMetaClass();会在某些情况下详细破坏.
谢谢.
以下是其他帖子的引文:
我正在一个iOS项目中工作,该项目包括由另一家公司创建的静态库.该库包含旧版AFNeworking,我没有任何源文件.
现在我需要使用更新的(并且更少的bug)版本的afneworking,但我不能在项目中两次包括相同的类(当然)因为所有"重复的符号"
我的问题是我正在准备一个iOS框架,我想在将来避免这种情况.我不是在谈论AFNetworking,而是其他非常流行的iOS框架.另外,我在原始框架代码中应用了一些自定义更改.
避免"重复符号"和"类X在Y和Z中实现的一种方法.将使用其中一个",我想到的是在原始框架类中添加一些前缀,但是这是正确的解?
更新1:
我试图应用约翰的解决方案,但没有快乐.我创建了一个简化的项目(这里是repo的链接),它有两个类FrameworkClass,它只存在于框架目标中,而SharedClass存在于框架和应用程序目标中,所以也许你可以看看我是否在做某事错误.应用程序启动后,我仍然得到:
objc[96426]: Class SharedClass is implemented in both .../TestFramework.framework/TestFramework and .../SymbolsVisibilityTest.app/SymbolsVisibilityTest. One of the two will be used. Which one is undefined
更新2:
这是我的输出nm基于提供的示例项目的框架输出:
0000000000007e14 t -[FrameworkClass doFramework]
0000000000007e68 t -[SharedClass doShared]
U _NSLog
U _NSStringFromSelector
00000000000081f0 s _OBJC_CLASS_$_FrameworkClass
U _OBJC_CLASS_$_NSObject
0000000000008240 s _OBJC_CLASS_$_SharedClass
00000000000081c8 s _OBJC_METACLASS_$_FrameworkClass
U _OBJC_METACLASS_$_NSObject
0000000000008218 s _OBJC_METACLASS_$_SharedClass
0000000000007fb0 s _TestFrameworkVersionNumber
0000000000007f70 s _TestFrameworkVersionString
U ___CFConstantStringClassReference
U __objc_empty_cache …Run Code Online (Sandbox Code Playgroud) objective-c static-libraries objective-c-runtime ios ios-frameworks
我试图使用这种方法:class_addMethod()在Obj-c中使用这样的方法:
class_addMethod([self class], @selector(eventHandler), imp_implementationWithBlock(handler), "v@:");
Run Code Online (Sandbox Code Playgroud)
我在Swift中使用它就像这样:
class_addMethod(NSClassFromString("UIBarButtonItem"), "handler", imp_implementationWithBlock(handler), "v@:")
Run Code Online (Sandbox Code Playgroud)
UIBarButtonItem正如您可能已经想到的那样,它是一个扩展.
imp_implementationWithBlock 采用类型的参数 AnyObject!
我怎么能()->()投入AnyObject?
我试图像这样抛出它:handler as AnyObject但是它给了我一个错误说:()->() does not conform to protocol 'AnyObject'
我理解的背景:Objective-C方法调用基本上是一个带有两个隐藏参数(接收器和选择器)的C函数调用.Objective-C运行时包含一个名为objc_msgSend()的函数,该函数允许以这种方式调用方法.不幸的是,当函数返回结构时,可能需要一些特殊处理.有一些神秘的(有些人可能会说是疯狂的)规则来控制结构是否像其他值一样被返回,或者它是否实际上是通过引用在隐藏的第一个参数中返回的.对于Objective-C,还有一个名为objc_msgSend_stret()的函数必须在这些情况下使用.
问题:给定一个方法,NSMethodSignature或其他什么可以告诉我是否必须使用objc_msgSend()或objc_msgSend_stret()?到目前为止,我们已经发现NSMethodSignature知道这一点,它在调试输出中打印它,但似乎没有公共API.
如果你想回答"你为什么要这样做?!",请在阅读之前阅读以下内容:https://github.com/erikdoe/ocmock/pull/41
64位运行时消除了直接访问对象的isa字段的能力,这是CLANG工程师一直警告我们的事情.它们被一组相当具有创造性(且神奇)的ABI规则所取代,这些规则关于新命名的isa标题的哪些部分包含有关该对象的信息,甚至包含其他状态(在NSNumber/NSString的情况下).似乎存在一个漏洞,因为你可以选择退出新的"魔法"isa并使用你自己的一个(原始的isa),代价是通过某些运行时代码路径走慢路.
我的问题是双重的,然后:
如果可以选择退出并将object_setClass()任意类放入对象中+allocWithZone:,是否也可以在类的额外空间中放置任何内容,或者运行时是否会尝试通过快速路径读取它?
isa标题中究竟是什么标记为让运行时将它与正常的isa区分开来?