Variadic method in Swift - objective-c

Objective C Code:
- (instancetype)initWithInts:(int32_t)int1, ... {
va_list args;
va_start(args, int1);
unsigned int length = 0;
for (int32_t i = int1; i != -1; i = va_arg(args, int)) {
length ++;
}
va_end(args);
...
...
return self;
}
This code is used to count the numbers of method's parameters.
Swift Code:
convenience init(ints: Int32, _ args: CVarArgType...) {
var length: UInt = 0
self.init(length: args.count)
withVaList(args, { _ in
// How to increase length' value in loop?
})
}
What's the best practise to use withVaList to loop through the argument list with a CVaListPointer? Help is very appreciated.

How about just
convenience init(args: Int...) {
return args.count
}

convenience required init(args: Int32...) {
}
If you define your func parameter following by three dots ..., you will notice args is actually a [Int32] type.
So just do casting likes Array, i.e. args.count, for i in args.

Related

Swift 4, reading byte data

so I recently update my IMAC and Xcode, after updating part of my code wasn't running as its supposed to it. Here is where I originally check messages.
func checkForMessages() {
while true {
if inputBuffer.length < 4 {
return
}
var msgLength = (inputBuffer.bytes).load(as: UInt32.self)
msgLength = UInt32(bigEndian: msgLength)
print("msgLength = \(msgLength)")
print("inputBuffer Length = \(inputBuffer.length)")
print("inputBuffer = \(inputBuffer)")
if inputBuffer.length < msgLength {
return
}
//print("data = \(inputBuffer.subdata(with: NSRange(location: 4, length: Int(msgLength))))")
if inputBuffer.length < msgLength + 4 {
return
}
let message: Data? = inputBuffer.subdata(with: NSRange(location: 4, length: Int(msgLength)))
processMessage(message!)
let amtRemaining: Int = inputBuffer.length - Int(msgLength) - 4
if amtRemaining == 0 {
inputBuffer = NSMutableData()
}
else {
print("Creating input buffer of length \(amtRemaining)")
inputBuffer = NSMutableData(bytes: inputBuffer.bytes + 4 + Int(msgLength), length: amtRemaining)
}
}
}
and then the process message function
func processMessage(_ data: Data) {
let reader = MessageReader(data: data)
print("this is the message data\(data)")
let msgType = reader?.readByte().hashValue
}
and then the actually MessageReader, its in Objective C, since I pulled it off the internet a while ago. Its been working fine for me ever since. Until now.
#import "MessageReader.h"
#implementation MessageReader
- (id)initWithData:(NSData *)data {
if ((self = [super init])) {
_data = data;
_offset = 0;
}
return self;
}
- (unsigned char)readByte {
unsigned char retval = *((unsigned char *) (_data.bytes + _offset));
_offset += sizeof(unsigned char);
return retval;
}
- (int)readInt {
int retval = *((unsigned int *) (_data.bytes + _offset));
retval = ntohl(retval);
_offset += sizeof(unsigned int);
return retval;
}
- (NSString *)readString {
int strLen = [self readInt];
NSString *retval = [NSString stringWithCString:_data.bytes + _offset encoding:NSUTF8StringEncoding];
_offset += strLen;
return retval;
}
- (void)dealloc {
}
#end
Now the issue is that instead of returning the number for say “1” or “2”, “30”, etc. its returning some huge number like 1836718193728. I believe the issue lies in the messageReader, readByte function.
From your comment, reader?.readByte().hasValue is returning a huge number. (I believe hasValue is just a typo and its hashValue.)
That's a possible behavior of hashValue.
Are you using hashValue as if it's a UInt8 to Int conversion tool?
It's wrong. The property hashValue is (and should be) implemented to return some Int value which fulfills one axiom:
where a == b, a.hashValue == b.hashValue
In older versions of Swift, UInt8.hashValue may have returned the same value of type Int, but you should not rely on such an implementation detail that is not documented. A slight change of implementation would lead to different results.
And in fact, Swift 4.2 has changed the implementation of hashValue drastically.
SE-0206 Hashable Enhancements
You may need to fix all parts of your project using hashValue.
Usually, you use Int.init(_:) to convert UInt8 to Int.
In a context like Optional Chaining as shown in your let msgType, you may need to write something like this.
let msgType = (reader?.getByte()).map{Int($0)}
If you have many parts using hashValue wrongly, better write an extension:
extension UInt8 {
var integerValue: Int {
return Int(self)
}
}
let msgType = reader?.getByte().integerValue
Generally, you should better not include such wrong hacks in your project.

Trouble converting NSData Objective-C code to Swift

I've been having issues converting an Objective-C snippet to Swift that uses NSData and CoreBluetooth. I have looked at this question and a couple others dealing with NSData in Swift but haven't had any success.
Objective-C Snippet:
- (CGFloat) minTemperature
{
CGFloat result = NAN;
int16_t value = 0;
// characteristic is a CBCharacteristic
if (characteristic) {
[[characteristic value] getBytes:&value length:sizeof (value)];
result = (CGFloat)value / 10.0f;
}
return result;
}
What I have so far in Swift (not working):
func minTemperature() -> CGFloat {
let bytes = [UInt8](characteristic?.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
value = Int16(fPointer.pointee)
result = CGFloat(value / 10) // not correct value
return result
}
Does the logic look wrong here? Thanks!
One error is in
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
because the rebound pointer $0 is only valid inside the closure and must
not be passed to the outside. Also the capacity should be 1 for a
single Int16 value. Another problem is the integer division in
result = CGFloat(value / 10)
which truncates the result (as already observed by the4kman).
Creating an [UInt8] array from the data is not necessary, the
withUnsafeBytes() method of Data can be used instead.
Finally you could return nil (instead of "not a number") if no
characteristic value is given:
func minTemperature() -> CGFloat? {
guard let value = characteristic?.value else {
return nil
}
let i16val = value.withUnsafeBytes { (ptr: UnsafePointer<Int16>) in
ptr.pointee
}
return CGFloat(i16val) / 10.0
}
You should make the return value optional and check if characteristic is nil in the beginning with a guard. You should also explicitly convert the value to CGFloat, then divide it by 10.
func minTemperature() -> CGFloat? {
guard characteristic != nil else {
return nil
  }
let bytes = [UInt8](characteristic!.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
let value = Int16(fPointer.pointee)
result = CGFloat(value) / 10
return result
}

Objective-C code (array.indexOfObjectPassingTest) to Swift

How can I use the Objective-C code below in Swift, I tried but something is wrong.
Objective-C:
NSUInteger index = [theArray indexOfObjectPassingTest:
^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop)
{
return [[dict objectForKey:#"name"] isEqual:theValue];
}
];
Swift (Doesn't work):
let index = theArray.indexOfObjectPassingTest { (var dict: NSDictionary, var ind: Int, var bool: Bool) -> Bool in
return dict.objectForKey("name")?.isEqual("theValue")
}
I played with it and got this to work:
let theArray: NSArray = [["name": "theName"], ["name": "theStreet"], ["name": "theValue"]]
let index = theArray.indexOfObjectPassingTest { (dict, ind, bool) in return dict["name"] as? String == "theValue" }
if index == NSNotFound {
print("not found")
} else {
print(index) // prints "2"
}
This can be further reduced. As #newacct mentioned in the comment, the return can be dropped since the closure is only a single line. Also, _ can be used in place of the parameters that aren't being used:
let index = theArray.indexOfObjectPassingTest { (dict, _, _) in dict["name"] as? String == "theValue" }
You can get rid of the parameter list in the closure entirely and use the default $0 value. Note in that case, the three parameters are combined as a tuple, so the first value of the tuple dict is referenced as $0.0:
let index = theArray.indexOfObjectPassingTest { $0.0["name"] as? String == "theValue" }
Swift 3:
Consider this method:
public func index(where predicate: (Element) throws -> Bool) rethrows -> Int?
The following gives you an example how to use it:
let dict1 = ["name": "Foo"]
let dict2 = ["name": "Doh"]
let array = [dict1, dict2]
let index = array.index { (dictionary) -> Bool in
return dictionary["name"] == "Doh"
}
This returns the value 1.
Hope that helps
Guess you need this:
var index: UInt = theArray.indexOfObjectPassingTest({(dict: [NSObject: AnyObject], idx: UInt, stop: Bool) -> BOOL in return dict.objectForKey("name").isEqual(theValue)
})
I ended up using this for a Swift5 project:
let index = self.info.indexOfObject(passingTest: { (obj:Any, ind:Int, stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
if let details = obj as? NSDictionary, let id = details["id"] as? Int
{
if (orderId == id)
{
stop.pointee = true
return true
}
}
return false;
})
Where orderId is id value of the object I wanted to find.

How to stop enumerateObjectsUsingBlock Swift

How do I stop a block enumeration?
myArray.enumerateObjectsUsingBlock( { object, index, stop in
//how do I stop the enumeration in here??
})
I know in obj-c you do this:
[myArray enumerateObjectsUsingBlock:^(id *myObject, NSUInteger idx, BOOL *stop) {
*stop = YES;
}];
In Swift 1:
stop.withUnsafePointer { p in p.memory = true }
In Swift 2:
stop.memory = true
In Swift 3 - 4:
stop.pointee = true
This has unfortunately changed every major version of Swift. Here's a breakdown:
Swift 1
stop.withUnsafePointer { p in p.memory = true }
Swift 2
stop.memory = true
Swift 3
stop.pointee = true
since XCode6 Beta4, the following way can be used instead:
let array: NSArray = // the array with some elements...
array.enumerateObjectsUsingBlock( { (object: AnyObject!, idx: Int, stop: UnsafePointer<ObjCBool>) -> Void in
// do something with the current element...
var shouldStop: ObjCBool = // true or false ...
stop.initialize(shouldStop)
})
The accepted answer is correct but will work for NSArrays only. Not for the Swift datatype Array. If you like you can recreate it with an extension.
extension Array{
func enumerateObjectsUsingBlock(enumerator:(obj:Any, idx:Int, inout stop:Bool)->Void){
for (i,v) in enumerate(self){
var stop:Bool = false
enumerator(obj: v, idx: i, stop: &stop)
if stop{
break
}
}
}
}
call it like
[1,2,3,4,5].enumerateObjectsUsingBlock({
obj, idx, stop in
let x = (obj as Int) * (obj as Int)
println("\(x)")
if obj as Int == 3{
stop = true
}
})
or for function with a block as the last parameter you can do
[1,2,3,4,5].enumerateObjectsUsingBlock(){
obj, idx, stop in
let x = (obj as Int) * (obj as Int)
println("\(x)")
if obj as Int == 3{
stop = true
}
}
Just stop = true
Since stop is declared as inout, swift will take care of mapping the indirection for you.

Objective-C block as return type

When I check out the src of Underscore project, then I found that the block somehow comprehensive for me:
typedef BOOL (^UnderscoreTestBlock)(id obj);
In the class, it define this class method:
+ (UnderscoreTestBlock (^)(UnderscoreTestBlock))negate
{
return ^UnderscoreTestBlock (UnderscoreTestBlock test) {
return ^BOOL (id obj) {
return !test(obj);
};
};
}
and how can I understand these code, Thanks!
I think that you got it right. negate is a class method which returns a block.
That returned block takes an UnderscoreTestBlock argument and returns another UnderscoreTestBlock. Example:
UnderscoreTestBlock isANumber = ^BOOL (id obj) {
return [obj isKindOfClass:[NSNumber class]];
};
BOOL b1 = isANumber(#123); // YES
BOOL b2 = isANumber(#"abc"); // NO
UnderscoreTestBlock isNotANumber = [Underscore negate](isANumber);
BOOL b3 = isNotANumber(#123); // NO
BOOL b4 = isNotANumber(#"abc"); // YES
[Underscore negate] returns a block that is called with the isANumber argument.
The return value of that call is the block
^BOOL (id obj) { return !test(obj); };
where test is the isANumber argument, which is captured by the block.
I am sure that there are good reasons why the Underscore project defines the negate
method like this, it is probably necessary for their "functional" notation.
Otherwise a slightly simpler way would be to define a method
+ (UnderscoreTestBlock)negateBlock:(UnderscoreTestBlock)test
{
return ^BOOL (id obj) {
return !test(obj);
};
}
which would be used like
UnderscoreTestBlock isNotANumber = [Underscore negateBlock:isANumber];