Ios 为什么,将nil作为参数从Objc发送到swift类初始值设定项,用新对象替换nil参数
我创建了这个Swift类:Ios 为什么,将nil作为参数从Objc发送到swift类初始值设定项,用新对象替换nil参数,ios,objective-c,swift,Ios,Objective C,Swift,我创建了这个Swift类: @objc public class Tester: NSObject { private var name: String private var user: Users init(string:String, user: Users) { print(user.empId) print(user.name) self.user = user self.name = string
@objc public class Tester: NSObject {
private var name: String
private var user: Users
init(string:String, user: Users) {
print(user.empId)
print(user.name)
self.user = user
self.name = string
super.init()
}
}
我这样称呼Obj C中的初始值设定项:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSString * nilString = nil;
Users * nilUser = nil;
Tester * test = [[Tester alloc] initWithString:nilString user:nilUser];
return YES;
}
这里,我将参数的nil
传递给Swift初始值设定项。理想情况下,我希望这会崩溃,因为初始值设定项只接受非nil
值
但实际情况是,当执行点到达初始值设定项内部时,将创建新的参数对象:
nil
字符串变为”
,原来是nil
的User
变量现在指向一个对象
但是像这样的房产
@property(nonatomic,strong) SomeClass * object;
在哪里
object = nil;
当我从swift中调用这个对象时
let x = object.someGetter;
这会崩溃
在某一点上,如果我将nil传递给某个非null,它会工作,而在另一点上,它会崩溃。为什么会有这种奇怪的行为?如果由于某些原因,我的参数为nil,并传递给nonnull,我希望它崩溃。这样我就能解决问题了
编辑:这变得出乎意料,进一步尝试使用此代码
字符串参数实际上是字符串,但用户显示未初始化,因此对字符串的操作效果很好,但用户对象接受了更改,但没有显示出来。当您说字符串是
”
,而用户是一个对象时,看起来您只是依赖于调试器的变量视图来实现这一点,对的因为控制台输出清楚地显示了两次nil
这里发生的事情是,如果值为nil
,初始化器生成的代码实际上不会执行任何会崩溃的操作。从语义上讲,是的,它是无效的,但是实际的机器代码没有做一些依赖于非nil假设的事情。对print()
的调用之所以有效,是因为我假设,即使值意外地为nil
,将参数转换为Any
存在式的代码也会正常工作,并生成nil
Any
至于debugger视图,它是一个调试器,它只是尽可能地解释变量,但它实际上并没有运行Swift标准库。我不完全清楚为什么
字符串
变量显示为”
,我猜是因为它总是显示一个字符串表示,并确定它没有数据(没有数据的字符串是”
)。对于用户
,它实际上显示为0x0000000000000000
,这是正确的,这意味着它是空指针(即nil
)
最后,
let x=object.someGetter
会崩溃,因为生成的代码依赖于非nil
的值(具体来说,它可能调用swift\u retain()
或swift\u unknownRetain()
,这要求参数为非-nil,因为它取消引用指针)。因此有三个问题:
- 首先,为什么访问用户属性不会崩溃
- 第二,为什么有一个空字符串而不是
1nil
- 第三,为什么分配(自定义类的)属性会崩溃
Users
类的属性时使用Objective C消息传递(我假设-Users
是一个ObjC类,如调试器输出中所示;基类NSObject
)。
在“反汇编”视图中,可以看到:
0x1000018be <+78>: movq 0x3e6e2b(%rip), %rsi ; "empId"
....
0x1000018d7 <+103>: callq 0x100361b10 ; symbol stub for: objc_msgSend
这里有趣的部分是\u无条件地从Objectivec
调用。这将在内部调用Swift.String函数\u cocoaStringToSwiftString\u NonASCII
,并检查源代码(),您可以看到以下内容:
0x100001f45 <+53>: callq 0x100021f50
; static (extension in Foundation):
;Swift.String._unconditionallyBridgeFromObjectiveC (Swift.Optional<__ObjC.NSString>) -> Swift.String
....
0x100001f5b <+75>: callq 0x100001870
; TestCalling.Tester.init (string : Swift.String, user : __ObjC.ObjCUser) -> TestCalling.Tester at SwiftClass.swift:14
@inline(never) @_semantics("stdlib_binary_only") // Hide the CF dependency
func _cocoaStringToSwiftString_NonASCII(
_ source: _CocoaString
) -> String {
let cfImmutableValue = _stdlib_binary_CFStringCreateCopy(source)
let length = _stdlib_binary_CFStringGetLength(cfImmutableValue)
let start = _stdlib_binary_CFStringGetCharactersPtr(cfImmutableValue)
return String(_StringCore(
baseAddress: start,
count: length,
elementShift: 1,
hasCocoaBuffer: true,
owner: unsafeBitCast(cfImmutableValue, to: Optional<AnyObject>.self)))
}
发生以下情况:
someGetter
的返回值将被保留(objc\u retainatoreleasedReturnValue
)--这不会崩溃x
是弱属性或可选属性,则代码不会崩溃。即使使用类型推断,它也不会崩溃(在我的机器上,swift 4):
这是因为x
的推断类型是可选的SomeClass?
,除非属性本身声明为nonnull
:
@property(nonatomic,strong, nonnull) SomeClass * object;
“因为控制台输出清楚地显示了两次nil。”这是针对第15行、第16行,它打印用户对象上的一些属性,而不是参数。我猜是因为它总是显示一个字符串表示,并且它确定它没有数据(没有数据的字符串是“”)-它尝试使用它,我想这里的字符串已经初始化了。检查问题的更新。您能详细说明属性访问部分吗?我不完全理解“someGetter的返回值将被保留(objc_retainustoreleasedReturnValue)——这不会崩溃”和“返回的对象将被隐式展开——然后崩溃”。为什么someGetter的返回值会被保留,为什么它会被隐式解包?
let x = object.someGetter;
@property(nonatomic,strong, nonnull) SomeClass * object;