为什么要从Objc C将nil作为参数发送到swift类初始化程序,用新对象替换nil参数

邦操作员

我创建了这个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字符串将成为""User那是可变nil现在指向的对象。

但是对于这样的财产

@property(nonatomic,strong) SomeClass * object;

哪里

object = nil;

当我迅速调用该对象时,

let x = object.someGetter;

这崩溃了。

At one point, If I pass nil to some non null, it works and at another point, it crashes. Why does this weird behavior exist? If for some reasons, my parameters are nil, and passed to nonnull, I would like this to crash. So that I can fix things.

EDIT: This became so unexpected, further trying to play with this code.

在此处输入图片说明

The string parameter was actually as string, but the User shows uninitialized, hence manipulations on string worked well, but the user object, took the changes but did not showed them back.

Andreas Oetjen

So there are three questions:

  • first, why does accessing the user properties not crash,
  • second, why is there an empty string instead of a nil one,
  • third, why does assigning a property (of a custom class) crash

I'll answer all of them :-)

1. Accessing the Users properties

Swift uses Objective C messaging when accessing the properties of the Users class, (I assume - Users is an ObjC-Class as seen in the debugger output; base class NSObject). In the disassembly view, one can see this:

0x1000018be <+78>:   movq   0x3e6e2b(%rip), %rsi      ; "empId"
   ....
0x1000018d7 <+103>:  callq  0x100361b10               ; symbol stub for: objc_msgSend

Since objc_msgSend supports nil messaging, the call does not fail.

2. Empty String magic

When calling the Swift initializer from Objective C, the bridging code creates the following:

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

The interesting part here is the _unconditionallyBridgeFromObjectiveC call. This will internally call the Swift.String function _cocoaStringToSwiftString_NonASCII, and checking the source code (here, line 48), you can see the following:

@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)))
}

The function always returns a new Swift.String object, in our case an empty one! So, again no crash.

3. Property access

When accessing a custom property, e.g. assigning it to a variable:

let x:SomeClass = object.someGetter;

发生以下情况:

  1. 的返回值someGetter将保留(objc_retainAutoreleasedReturnValue)-这不会崩溃

  2. 返回的对象将被隐式展开-然后崩溃

如果x是弱属性或可选属性,则代码不会崩溃。即使使用类型推断,它也不会崩溃(在我的机器上为swift 4):

let x = object.someGetter;

这是因为的推断类型x是可选的,SomeClass?除非属性本身声明为nonnull

@property(nonatomic,strong, nonnull) SomeClass * object;

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么要从C ++接口继承

在python中,为什么要从内置模块导入“对象”?

为什么只有在括号初始化对象时才需要从 int 到 float 的缩小转换?

为什么Swift Int零在objc代码中读取为nil?

如果Object :: try被发送到nil对象,为什么会起作用?

为什么我们需要从网上下载程序来编程?

为什么C ++要求我在初始化列表中重复基类的模板参数?

在 Ruby 中,当初始化一个只包含 nil 的新类时,为什么在我将一个字符串铲入其中后仍然返回 nil?

将@objc方法作为操作添加到按钮并获取“'#selector'的参数不引用'@objc'方法,属性或初始化程序”

在Objective-c中,当找不到响应者时,应用程序崩溃,但是当消息发送到nil对象时,应用程序不会崩溃。这是为什么?

为什么在从数组wrt中删除要从数组中删除项目之前,必须从参数中删除?

为什么ProcessExecutionEngine总是将空输入参数发送到Web服务?

ObjC-为什么实例被允许访问.m文件中的类扩展属性?

为什么GCD,ObjC和Swift之间的性能差距如此之大

为什么要从公用程序调用私有函数,而不是在公用程序中实现代码?

为什么可变参数函数不能“吃掉” C ++ 11中的列表初始化参数?

为什么BIOS需要从MBR加载第一级引导加载程序,而UEFI却不是这种情况?

为什么我需要从另一个来源指向 Array 构造函数,为什么我不能直接在数组对象上调用 toString() ?

为什么每次都使用默认参数?初始化类

执行我的Objective-C程序时,为什么会出现错误“无法初始化类对象”?

发送消息objc_msgSend(class,@ selector(dealloc))释放对象,为什么访问对象指针错误?

我的 C# 程序没有按照我认为的对象初始化顺序初始化对象。为什么?

为什么我们需要对象发送者作为 c# 中的参数事件处理程序?

为什么可以将未初始化的变量地址传递给函数的指针参数

为什么在Ruby中未初始化的实例变量返回nil,但是未初始化的类变量引发错误?

为什么指定的初始化被gcc接受却没有效果,并且生成的聚合以某种方式作为参数传递给类构造函数

为什么我的值未由初始化(参数化)构造函数初始化?

为什么我可以将@objc协议声明为弱属性,但不能将Swift协议声明为弱属性?

对象初始化-为什么用“:”而不是“ =”初始化对象属性