Ios 为什么,将nil作为参数从Objc发送到swift类初始值设定项,用新对象替换nil参数

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

我创建了这个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
        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,因为它取消引用指针)。

因此有三个问题:

  • 首先,为什么访问用户属性不会崩溃
  • 第二,为什么有一个空字符串而不是
    nil
    1
  • 第三,为什么分配(自定义类的)属性会崩溃
我会回答所有问题:-)

1.访问用户属性 Swift在访问
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;