我创建了这个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.
So there are three questions:
nil
one,I'll answer all of them :-)
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.
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.
When accessing a custom property, e.g. assigning it to a variable:
let x:SomeClass = object.someGetter;
发生以下情况:
的返回值someGetter
将保留(objc_retainAutoreleasedReturnValue
)-这不会崩溃
返回的对象将被隐式展开-然后崩溃
如果x
是弱属性或可选属性,则代码不会崩溃。即使使用类型推断,它也不会崩溃(在我的机器上为swift 4):
let x = object.someGetter;
这是因为的推断类型x
是可选的,SomeClass?
除非属性本身声明为nonnull
:
@property(nonatomic,strong, nonnull) SomeClass * object;
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句