1

I'm converting a project from Objective-C to Swift. In it, there is a NSMutableData object that was originally instantiated like this:

NSMutableData *buffer = [[NSMutableData alloc] initWithLength:65535];

In Swift, when I try to instantiate it the same way, it produces an optional (and the function I'm using it in doesn't work with an optional, so it gets a bit messy if I do it this way unless I can be sure it's safe to force unwrap it):

let buffer = NSMutableData(length: 65535) // optional

But I can do what appears to be the exact same thing in two steps and get a non-optional:

let buffer = NSMutableData() // not optional
buffer.length = 65535

As far as I can tell, I get the same result either way, so why is only the first one optional? Is there any reason it wouldn't be safe to force unwrap it, or any disadvantage to doing it the second way?

John Montgomery
  • 6,739
  • 9
  • 52
  • 68
  • 4
    I think the most likely answer would be that NSMutableData would return nil if not enough memory could be allocated. I would probably never use the setter directly, just to be safe, but instead might use something like `if let buffer = NSMutableData(length: 65535) { ... }` – Wolfgang Schreurs Jun 09 '17 at 21:06

2 Answers2

2

I guess that init(length:) is the safe way of trying to allocate a certain number of bytes, that may safely fail and return nil. But if you change the length property of your mutable data object and malloc fails, then an exception is thrown.

If I needed to allocate a huge number of bytes and I was not sure if malloc will succeed, then I'd use a guard statement:

guard let buffer = NSMutableData(length: N) else { return }
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
  • Hmm, that could be it. But the files I'm working with only get up to a couple megabytes, so probably no risk? – John Montgomery Jun 09 '17 at 21:20
  • @JohnMontgomery Depends: does the users have the possibility of creating multiple files until the memory runs out? also if you are programming an iOS or OS X application may make the difference. – Ramy Al Zuhouri Jun 09 '17 at 21:26
  • 1
    It's a couple of files downloaded from a server, on iOS. But I think I can work in the `guard` regardless, just for the sake of safety. – John Montgomery Jun 09 '17 at 21:33
2

The documentation states: The returned object has the same memory alignment guarantees as malloc(_:). And malloc might fail: If the function failed to allocate the requested block of memory, a null pointer is returned. In Objective-C this would mean that initWithLength: would return nil — as there is no language feature to express this, this might go unnoticed.
But in swift such language feature does exist: Optionals and optional initialisers.
So Objective-C and Swift here do the same, but only Swift is honest about it, while in Objective-C you might get a nil without realising it.

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
  • 2
    In Objective-C, this constructor is defined as `(nullable instancetype)`. But even before nullability notation, initializers are allowed to return `nil`. Your Swift discussion is fine, but calling Objective-C dishonest about this is incorrect. In the past, any object reference could have been nil. – Steven Fisher Jun 09 '17 at 21:33
  • What I want to highlight in my answer is the fact, that swift forces the programmer to think about the nil-case. objc doesn't do that, even with the nullability tags, as this only expresses that a result MIGHT be nil. In swift you will know that something IS nil. – vikingosegundo Jun 09 '17 at 21:37