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

前端之家收集整理的这篇文章主要介绍了ios – 为什么,将nil作为参数从Objc C发送到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变量现在指向一个对象.

但是对于像这样的房产

@property(nonatomic,strong) SomeClass * object;

哪里

object = nil;

当我从swift调用这个对象时,

let x = object.someGetter;

这崩溃了.

有一次,如果我将nil传递给某些非null,它会起作用,而在另一点上,它会崩溃.为什么存在这种奇怪的行为?如果由于某些原因,我的参数是nil,并传递给nonnull,我希望这会崩溃.这样我就能解决问题.

编辑:这变得如此意外,进一步尝试使用此代码.

字符串参数实际上是字符串,但是User显示未初始化,因此对字符串的操作运行良好,但是用户对象接受了更改,但没有显示它们.

解决方法

所以有三个问题:

>首先,为什么访问用户属性不会崩溃,
>第二,为什么有一个空字符串而不是一个空字符串,
>第三,为什么分配属性(自定义类)崩溃

我会回答所有这些:-)

1.访问“用户属性

当访问Users类的属性时,Swift使用Objective C消息传递(我假设 – 用户是调试器输出中看到的ObjC-Class;基类NSObject).
在反汇编视图中,可以看到:

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

由于objc_msgSend支持nil消息传递,因此调用不会失败.

2.空字符串魔法

从Objective C调用Swift初始化程序时,桥接代码会创建以下内容

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

这里有趣的部分是_unconditionallyBridgeFromObjectiveC调用.这将在内部调用Swift.String函数_cocoaStringToSwiftString_NonASCII,并检查源代码(here,line 48),您可以看到以下内容

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

函数总是返回一个新的Swift.String对象,在我们的例子中是一个空对象!所以,再没有崩溃.

3.财产准入

访问自定义属性时,例如将其分配给变量:

let x:SomeClass = object.someGetter;

发生以下情况:

> someGetter的返回值将被保留(objc_retainAutoreleasedReturnValue) – 这不会崩溃
>返回的对象将被隐式解包 – 然后崩溃

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

let x = object.someGetter;

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

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

猜你在找的iOS相关文章