In Objective-C, I might use +[NSValue valueWithNonretainedObject:] to keep a unique ID of an object, where I don't wish to retain the object itself. Seems like that's deprecated for Swift.
How to do in Swift?
It's still there. It's just been adjusted to be one of NSValue's initializers:
let anObject = "Hello!"
let value = NSValue(nonretainedObject: anObject)
This works:
let foo: NSString = "hello"
let fooval: NSValue = NSValue(nonretainedObject: foo)
fooval.description // evaluates to "<5040d191 b87f0000>", address of `foo`
Related
I'm trying to build an Objective-C block in Swift 2 in order to add it to an NSArray like so :
typealias CompletionBlock = () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
nsArray.addObject(aBlock) // Error
I know it will work just fine with a Swift array, but I need an NSArray here for compatibility with existing Objective-C code. And if I use a swift array the compiler will refuse to cast it to an NSArray because it won't be a [AnyObject] (it will be a [Any]).
The problem here is that a swift closure is not an object contrary to Objective-C blocks which are objects behind the scene (they are instances of NSBlock which is a subclass of NSObject)
So my question is : How do a create an Objective-C block in swift ? I've tried using #convention (block) in the typealias but it doesn't work.
EDIT : As of Swift 3, this is completely unnecessary (and doesn't even work). Adding closures to Objective-C arrays works out of the box in Swift 3. The answer below is valid for Swift 2 only.
I know this is a duplicate but I will still post a refactored answer from swift-closure-as-anyobject and cast-closures-blocks in case anyone lands on this one first.
The solution is to use the unsafeBitCast function to convert the Swift closure to an Objective-C compatible object before adding it to an NSArray and back before using it in Swift.
// The `#convention(block)` is important here in order to get
// Objective-C like memory management
typealias CompletionBlock = #convention(block) () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
let blockObject = unsafeBitCast(aBlock, AnyObject.self)
nsArray.addObject(blockObject)
let closureObject = nsArray[0]
let closure = unsafeBitCast(closureObject, CompletionBlock.self)
closure()
I'm unfamiliar with Objective C.
I'm using a private framework and need to be able to change one of the properties from within my Swift code.
The property is declared in Objective C this way:
#property (nonatomic, assign) BOOL *isSSNField;
in swift I am trying to change the value of the property this way:
myClass.isSSNField = true
I am getting this error
Cannot assign a value of type 'Bool' to a value of type 'UnsafeMutablePointer<ObjcBool>'
I'm not sure where to go from here, or why I'm getting this error at all
Update for Swift 5.1
For pointer types Swift provides pointee property,
Documentation for v5.1 says it is used for accessing instance referenced by this pointer
You can easily set the pointee field
myClass.isSSNField.pointee = false
And this is same for all pointer types and conversions. If you want to check the value of an Objective C BOOL* You can easily
if myClass.isSSNField.pointee.boolValue
I've never seen anything like the situation you describe, and personally I'm tempted to say the situation doesn't exist; I have never seen a BOOL* property or ivar in Objective-C in my life (and I'm darned old, believe me). However, if you insist: I haven't tested this, but I think you could say something like this:
var ok = UnsafeMutablePointer<ObjCBool>.alloc(1)
ok[0] = false // or true
let val = ok
myClass.isSSNField = val
However, although I think that will compile, I'm rather unclear on what the implications of doing it would be. It could cause the universe to collapse to a black hole, so be careful.
BOOL* in Objective-C is a pointer of Bool.
Use UnsafeMutablePointer in Swift.
var isSSNField: ObjCBool = true
myClass.isSSNField = &isSSNField
Then fix it.
Just like the isDirectory parameter in function:
func fileExists(atPath path: String, isDirectory:UnsafeMutablePointer<ObjCBool>?) -> Bool
You can use the code:
var isDirectory:ObjCBool = true
var isFileExists = FileManager.default.fileExists(atPath: <#YourPath#>, isDirectory: &isDirectory)
The safer way to handle this would be very similar to #TanJunHao 's answer:
var isSSNField = ObjCBool(true)
myClass.isSSNField = &isSSNField
Look like your Objective C property declaration is incorrect. Please update like below and try
#property (nonatomic, assign)BOOL isSSNField;
I enjoy using the new Optional class in Java. Is there an equivalent in Objective C?
I need something that can hold a small value like nil until I try to get its value, at which point it is initialized and has the new value cached for next time I read it. I don't want to check if the object is nil at every point where I try to read its value.
You can lazy load the variable using a getter.
- (MyClass *) something {
if(!_something) {
_something = [MyClass new];
}
return _something;
}
Thus, each time you use instance.something, it will do the checking for you and load the object if it's not there already.
If it's a simple one-liner and you simply don't want to use if, you can skip out the keyword (I hear this is quicker, but can't verify that now):
- (MyClass *) something {
return _something ?: (_something = [MyClass new]);
}
This is very similar to the unwrapping in Swift where myObject?.aValue will return aValue only if myObject != nil. Or the if let statement: if let value = myObject?.aValue
In objective C, there is no specific syntax dedicated to this however you can easily test for existence using simple if statement e.g.: if(myObject). Because Objective-C objects are pointers and the address of a NULL pointer is 0x0 this if statement will evaluate to false if myObject is NULL (or nil if you like).
If you try to read a property of a nil object you will likewise get nil (for properties that are also objects). And if you try to set a nil object's property, nothing will happen.
I like to use the ternery operator as much as possible e.g.:string != nil ? [textField setText:string] : NULL;
As suggested in previous answers you can use lazy instantiation in your specific situation.
This is the userinfo of my silent push notification. How to get value of result in objective-c?
{
aps = {
"content-available" = 1;
result = STOP;
sound = "";
};
}
With KVC you can dive into the nested dictionary with one method call.
[userInfo valueForKeyPath:#"aps.result"];
The accepted answer uses KVC's valueForKey:. Please be aware, that there are differences to NSDictionary's own method objectForKey:. While the first one only takes NSStrings as needed in the question, the second one takes any object that conforms to NSCopying protocol (id<NSCopying>). NSString, NSNumber, NSDate,... do and it is easy to implement NSCopying for your own classes.
Also NSDictionary supports subscription. from the docs:
objectForKeyedSubscript:
Returns the value associated with a given key.
Discussion
This method behaves the same as objectForKey:
Unless you want to do something KVC-specific as in my answer, you should prefer objectForKey: and actually you than could write the compact code
id obj = userInfo[#"aps"][#"result"];
or if you know the class of the object (NSString in your example)
NSString *obj = userInfo[#"aps"][#"result"];
NSLog(#"%#",[[Response valueForKey:#"aps"] valueForKey:#"result"]);
I am using both swift and objective-c in my app.
I have a CustomClass and I want to create a swift array for the class and add content to it from my objective-c class called oldClass that has an array of these objects in a NSArray called arrayOfCustomClass.
var newArray = [CustomClass]()
newArray += oldClass.arrayOfCustomClass
This causes an error:
'[(CustomClass)]' is not identical to 'CGFloat'
Any help?
thanks
Reza
The problem is that Swift knows nothing of what's in an NSArray. You must cast the NSArray explicitly to a [CustomClass] (and you'd better not be lying or you'll crash at runtime).
What seems to work is:
newArray += oldClass.arrayOfCustomClass as AnyObject as [CustomClass]
To do this safely you just need to try an optional cast. If you think the NSArray only has elements of type CustomClass, you can do this:
var newArray = [CustomClass]()
if let customArray = oldClass.arrayOfCustomClass as? [CustomClass] {
newArray += customArray
}
If you want to extract the CustomClass elements (a little different from what you asked, I know), this is the way:
var newArray = [CustomClass]()
for element: AnyObject in oldClass.arrayOfCustomClass {
if let custom = element as? CustomClass {
newArray.append(custom)
}
}