Recently I saw the following code line in a book (about CoreData)
return modelURLs(in: modelName).compactMap(NSManagedObjectModel.init)
I know what the code does but the question is: Why and how does it work?
There should be a closure as the argument of the compactMap function but there's only a "NSManagedObjectModel.init" in NORMAL parenthesis. What's the secret about it? What is it doing there? I would understand it if there's a static/class property called init which returns a closure but I don't think there is.
Unfortunately the book doesn't say more about this line of code. I would like to have further readings from the apple docs but I can't find anything. When I make a google search about "init in closures" then I don't get helpful results.
So you guys are my last hope :)
By the way: the function modelURLs(in: modelName) returns an Array of URLs but that's not really important here.
When using closures different syntax can be used as in the below example that converts an int array to a string array
let array = [1, 2, 3]
The following calls to compactMap will all correctly convert the array and generate the same result
let out1 = array.compactMap({return String($0)})
let out2 = array.compactMap({String($0)})
let out3 = array.compactMap {String($0)}
let out4 = array.compactMap(String.init)
When there are two init methods that takes the same number and types of argument then you must add the full signature for the init method to use. Consider this simple example struct
struct TwoTimesInt: CustomStringConvertible {
let value: Int
let twiceTheValue: Int
var description: String {
return "\(value) - \(twiceTheValue)"
}
init(value: Int) {
self.value = value
self.twiceTheValue = 2 * value
}
}
With only 1 init method we can do
let out5 = array.compactMap(TwoTimesInt.init)
But if we add a second init method
init(twiceTheValue: Int) {
self.value = twiceTheValue / 2
self.twiceTheValue = twiceTheValue
}
Then we need to give the full signature of the init method to use
let out6 = array.compactMap( TwoTimesInt.init(value:) )
Another thing worth mentioning when it comes to which method is selected is to look at the full signature of the init method including if it returns an optional value or not. So for example if we change the signature of the second init method to return an optional value
init?(twiceTheValue: Int) {
self.value = twiceTheValue / 2
self.twiceTheValue = twiceTheValue
}
then compactMap will favour this init since it expects a closure that returns an optional value, so if we remove the argument name in the call
let out7 = array.compactMap(TwoTimesInt.init)
will use the second init while the map function on the other hand will use the first init method if called the same way.
let out8 = array.map(TwoTimesInt.init)
Related
I'm trying implement generic storage of configuration parameters by using class type string as a dictionary key. The idea is that the retrieve function will return an object of proper type. Each type is unique in the storage. However, when I call the function, I'm getting a Swift compiler error and am not sure how interpret it:
Compiler Error:
Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
I checked the documentation, and it seems like the type(of:) method is supposed to return runtime class, while the compiler makes it look like it's complaining because it thinks I'm passing a type of Any
How do I pass Swift class name as a function parameter without using an instance of that class?
func retrieve<T>(type: T.Type) -> T? {
let valueKey = String(describing: type)
print("retrieving: \(valueKey)")
return dictionary[valueKey] as? T
}
func update(with value: Any) {
let valueKey = String(describing: type(of: value))
print("Updating: \(valueKey)")
dictionary[valueKey] = value
}
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
subject.update(with: testCase)
//Compiler Error: Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
let t = type(of: testCase)
let retrieved = subject.retrieve(type: t)
//check for equality
}
//this works
expect(subject.retrieve(type: Int.self)).to(equal(1))
expect(subject.retrieve(type: Double.self)).to(equal(2.0))
expect(subject.retrieve(type: String.self)).to(equal("text"))
I've done more testing, and see that it appears my array does not honor the type(of: ) documentation, and this function returns the same object as "Any":
func retrieve<T>(sample: T) -> T? {
let valueKey = String(describing: type(of: sample))
print("retrieving: \(valueKey)") //always retrieves same object "Any"
return dictionary[valueKey] as? T
}
Updated: Thank you for responses, to clarify - test cases were intended to begin with simple types, then progress to more complex classes. The actual implementation would store completely unique instances of custom types, not Strings or Ints.
let testCases: [Any] = [ConnectionConfig(...),
AccountID("testID"),
AccountName("testName")]
The tests recognize the generic nature of the retrieve function and assign appropriate types, as evidenced by code completion:
expect(subject.retrieve(type: ConnectionConfig.self)?.ip).to(equal("defaultIP"))
expect(subject.retrieve(type: AccountID.self)?.value).to(equal("testId"))
The intended end use within RxSwift context: provide the generic storage to a class and allow it to pull the appropriate values for configuration parameters. If no value exists, an error is thrown and is handled by a separate error handler:
class RxConfigConsumer: ConfigConsumer {
var connection: ConnectionConfig?
var accountID: AccountID?
init(with provider: ConfigProvider) {
connection = provider.retrieve(type: ConnectionConfig.self)
accountID = provider.retrieve(type: AccountID.self)
//etc
}
}
The combination of a generic with a metatype (.Type) is very weird and is probably what's tripping you up. If you get rid of the generic things work as you would expect:
func retrieve(_ T:Any.Type) {
print(type(of:T))
}
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
retrieve(type(of:testCase))
}
// Int.Type, Double.Type, String.Type
If you really want the generic, then get rid of the .Type and write it like this:
func retrieve<T>(_ t:T) {
print(type(of:t))
}
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
retrieve(type(of:testCase))
}
// Int.Type, Double.Type, String.Type
Even then, however, it's unclear to me what the point is of passing the metatype.
The short answer is what you're trying to do is impossible, because there is no way to type-annotate the following line of code:
let retrieved = subject.retrieve(type: t)
What is the static type, known at compile-time, of retrieved? It can't change at run-time. It certainly can't change from iteration to iteration. The compiler needs to allocate space for it. How much space does it require? Does it require space on the stack or heap? There's no way to know. The best we can say is that it's Any and put a box around it. 1 doesn't even have a proper type anyway. It's just an integer literal. It could be a Float or many other things (try let x: Float = 1 and see).
The answer is you can't build a loop like this. Your individual test cases are the right ones. Once you create an [Any], it is very difficult to get "real" types back out. Avoid it. If you have a more concrete problem beyond the example you've given, we can discuss how to deal with that, but I believe outside of a unit test, this specific problem shouldn't come up anyway.
This is an interesting question and you can run the following codes in a playground.
The first step is to solve the T.Type parameter. It's hard to put it into a function call. So to achieve your goal, we can use T but T.Type.
class MySubject {
var dictionary : [String : Any] = [:]
func retrieve<T>(type1: T) -> T? {
let valueKey = String(describing: (type(of: type1)))
print("retrieving: \(valueKey)")
return dictionary[valueKey] as? T
}
func update(with value: Any) {
let valueKey = String(describing: type(of: value))
print("Updating: \(valueKey)")
dictionary[valueKey] = value
}
}
var subject : MySubject = MySubject()
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
subject.update(with: testCase)
//Compiler Error: Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
let retrieved = subject.retrieve(type1: testCase)
//check for equality
}
The compilation is correct. But as you said, the return value is nil as a result of a generic retrieve Function. In order to achieve your goal, we may skip the generic way, use Any directly.
class MySubject {
var dictionary : [String : Any] = [:]
func retrieve(type1: Any) -> Any? {
let valueKey = String(describing: (type(of: type1)))
print("retrieving: \(valueKey)")
return dictionary[valueKey]
}
func update(with value: Any) {
let valueKey = String(describing: type(of: value))
print("Updating: \(valueKey)")
dictionary[valueKey] = value
}
}
var subject : MySubject = MySubject()
let testCases: [Any] = [1, 2.0, "text"]
for testCase in testCases {
subject.update(with: testCase)
//Compiler Error: Cannot invoke 'retrieve' with an argument list of type '(type: Any.Type)
let retrieved = subject.retrieve(type1: testCase)
//check for equality
}
Currently everything is perfect as you wish. But this brings up an interesting thought about generic. Is it O.K. or right to use generic here? As we know, there is a presumption in generic, which is represented by letters T, U, V. They have one common meaning: Type. When we try to use generic, we assume every parameter should have only one unique type. So in first case, Any is the only type and should be accepted without question in a generic call. There is no other type will be revealed during function call.
This kind of misunderstanding roots from the use of "let testCases: [Any] = [1, 2.0, "text"]. Although swift allows you to write this way, they are not a normal array. They are a list which contains different type essentially. So you can ignore fancy generic here without any regrets. Just pick the Any to solve your problem.
I'm trying to run this code but it's yielding unexpected results.
class Test: NSObject {
#objc var property: Int = 0
}
var t = Test()
t.perform(#selector(setter: Test.property), with: 100)
print(t.property)
The value being printed is some junk number -5764607523034233277. How can I set the value of a property using the perform method?
The performSelector:withObject: method requires an object argument, so Swift is converting the primitive 100 to an object reference.
The setProperty: method (which is what #selector(setter: Test.property) evaluates to) takes a primitive NSInteger argument. So it is treating the object reference as an integer.
Because of this mismatch, you cannot invoke setProperty: using performSelector:withObject: in a meaningful way.
You can, however, use setValue:forKey: and a #keyPath instead, because Key-Value Coding knows how to convert objects to primitives:
t.setValue(100, forKey: #keyPath(Test.property))
You can't use perform(_:with:) to do that since Int is not a NSObject subclass which is necessary for the arg. To specify the type you can workaround it with:
let setterSelector = #selector(setter: Test.property)
let setterIMP = t.method(for: setterSelector)
//Invoking setter
unsafeBitCast(setterIMP,to:(#convention(c)(Any?,Selector,Int)->Void).self)(t, setterSelector,100)
let getterSelector = #selector(getter: Test.property)
let getterIMP = t.method(for: getterSelector)
//Invoking getter
let intValue = unsafeBitCast(getterIMP,to:(#convention(c)(Any?,Selector)->Int).self)(t, getterSelector)
You can find more details and examples in my answer here
I am quite new in Swift. And I create a class(for example):
class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
}
and I also build a in other class function:
class func A_plusplus(f:Fraction){
f.a++
}
Then in the executive class I write:
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object)
print("after ran func =" + object.toString() )
So the console output is
before run func = 10; after ran func =11
The question is how can I just send a copy of the "object" to keep its value which equal to 10
And if functions are always pass-by-reference, why we still need the keyword: "inout"
what does difference between A_plusplus(&object)//[if I make the parameter to be a inout parameter] and A_plusplus(object)
Universally, I don't want to use struct. Although this will solve my
problem exactly, I do pass-by-value rarely.So I don't want program's
copying processes slow my user's phone down :(
And It seems conforming the NSCopying protocol is a good option.But
I don't know how to implement the function:
func copyWithZone(zone:
NSZone)-> AnyObject? correctly
If your class is subclass of NSObject,better to use NSCopying
class Fraction:NSObject,NSCopying{
var a:Int
var b:NSString?
required init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copyWithZone(zone: NSZone) -> AnyObject {
let theCopy=self.dynamicType.init(a: self.a)
theCopy.b = self.b?.copy() as? NSString
return theCopy
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
f.b = "after"
}
}
var object = Fraction(a:10)
object.b = "before"
print("before run func = " + object.toString())
print(object.b!) //“Before”
XXXclass.A_plusplus(object.copy() as! Fraction)
print("after ran func =" + object.toString() )
print(object.b!)//“Before”
If it is just a common swift class,You have to create a copy method
class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copy()->Fraction{
return Fraction(a: self.a)
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
}
}
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object.copy())
print("after ran func =" + object.toString() )
To make it clear,you have to know that there are mainly two types in swift
Reference types. Like Class instance,function type
Value types,Like struct and others(Not class instance or function type)
If you pass in a Reference types,you pass in the copy of Reference,it still point to the original object.
If you pass in a Copy type,you pass in the copy of value,so it has nothing to do with the original value
Let us talk about inout,if you use it,it pass in the same object or value.It has effect on Value type
func add(inout input:Int){
input++
}
var a = 10
print(a)//10
add(&a)
print(a)//11
Swift has a new concept so called "struct"
You can define Fraction as struct (Not class)
And
struct Fraction{
...
}
var object = Fraction(a:10)
var object1 = object //then struct in swift is value type, so object1 is copy of object (not reference)
And if you use struct then try to use inout in A_plusplus function
Hope this will help you.
how can I just send a copy of the "object" to keep its value which equal to 10
In Swift classes and functions are always passed by reference. Structs, enums and primitive types are passed by value. See this answer.
You can't pass an object by value. You would have to manually copy it before passing it by reference (if that's what you really want).
Another way is to turn your class into a struct, since it would then be passed by value. However, keep in mind there a few other differences between classes and structs, and it might not necessarily be what you want.
And if functions are always pass-by-reference, why we still need the keyword: "inout"
According to the swift documentation, inout is used when
you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
So in practice with inout you can pass a value type (such as struct or primitive) by reference. You shouldn't really use this very often. Swift provides tuples, that could be used instead.
what does difference between A_plusplus(&object)//[if I make the parameter to be a inout parameter] and A_plusplus(object)
There is no difference for your A_plusplus function. In that function you don't modify the parameter f itself, you modify the f.a property.
The following example shows the effect of using inout when passing a class object. Both functions are the same, differing only in its parameter definition.
class Person {
var name: String
init(name: String) { self.name = name }
}
var me = Person(name: "Lennon") // Must be var to be passed as inout
// Normal object by reference with a var
func normalCall(var p: Person) {
// We sure are able to update p's properties,
// and they will be reflected back to me
p.name = "McCartney"
// Now p points to a new object different from me,
// changes won't be reflected back to me
p = Person(name: "Ringo")
}
// Inout object reference by value
func inoutCall(inout p: Person) {
// We still can update p's properties,
p.name = "McCartney"
// p is an alias to me, updates made will persist to me
p = Person(name: "Ringo")
}
print("\(me.name)") //--> Lennon
normalCall(me)
print("\(me.name)") //--> McCartney
inoutCall(&me)
print("\(me.name)") //--> Ringo
In normalCall p and me are different variables that happen to point to the same object. When you instantiate and assign a new object to p, they no longer refer to the same object. Hence, further changes to this new object will not be reflected back to me.
Stating that p is a var argument just means that its value can change throughout the function, it does not mean the new value will be assigned to what was passed as argument.
On the other hand, in inoutCall you can think of p and me as aliases. As such, assigning a new object to p is the exact same as assigning a new object to me. Any and every change to p is persisted in me after the function ends.
It seems to me that there is a discrepancy in Swift's syntax between calling an initializer and a function with at least one paremeter.
Let's consider these two examples:
class SimpleClass {
var desc: String
init(desc: String) {
self.desc = desc
}
}
let aClass = SimpleClass(desc: "description")
and
func simpleFunc(a: Int, b:Int) -> Int {
return a + b;
}
let aVal = simpleFunc(5, b: 6)
It seems odd to me that the compiler forces you to omit the first label in a function call, otherwise you will get an error "Extraneous argument label 'a:' in call". Whereas if we want to omit the first label during initilization, you get the error "Missing argument label 'desc:' in call".
The language guide says:
When calling a function with more than one parameter, any argument after the first is labeled according to its corresponding parameter name.
Source: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html
The arguments to the initializer are passed like a function call when
you create an instance of the class.
Source: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html
I'm new to Swift so I hope I didn't miss something, but this seems like a syntax discrepancy, because initializers/ constructors are just kind of functions and forcing to omit the first label in a function call seems inconsistent to me.
That's because Swift focuses on readability; function calls to be able to be read like a sentence. See this, specifically the section on "Local and External Parameter Names for Methods". Your function, to comply with this style, should be more like:
func add(a: Int, to b: Int) -> Int {
return a + b
}
let c = add(1, to: 2)
I have a problem with a closure that is meant to be created and then being executed within another function over the range of the 2D pixel raster of an image where it shall basically called like this:
filter(i,j) and return a value based on its arguments.
I thought this code should work but it complains that the closure variable I have created is not initialized. I guess that means that I did not gave it arguments, but I wont within this function as the data is known to the closure at the time when it interacts with the image. How can I setup a closure which does not care about initialization?
Thank you in advance :)
func processFilter(type:FilterType){
var x = 0
var y = 0
//create cloure
var closure:(i:Int, j:Int)->Int
if(type == FilterType.MyFilter) {
x = 1024
y = 2048
func filter(i:Int, j:Int)->Int {
return i*j*x*y*4096
}
//compiler does not complain here...
closure = filter
}
//other if statements with different closure definitions follow...
//This call throws error: variable used before being initialized
let image = filterImage(closure)
}
You use the variable closure before the compiler is certain that it is initialized. You can solve this in 2 ways, depending on what you need:
Add an else-clause to your if and set closure to a default closure.
Make closure optional by defining it as var closure: ((i: Int, j: Int) -> Int)? and then you can check if it is optional before using it by using closure?(i, j) or if let filter = closure { filter(i, j)}.
Also, try to use better variable names such as filterClosure. closure on its own doesn't really say much.
The problem is that you define your closure as:
var closure:(i:Int, j:Int)->Int
Then you initialize it only if you enter the if
If not, that var is not initialized, hence the compiler warning
Possible solution:
func processFilter(type:FilterType){
var x = 0
var y = 0
//create cloure
var filterClosure:((i:Int, j:Int)->Int)?
if(type == FilterType.MyFilter) {
x = 1024
y = 2048
func filter(i:Int, j:Int)->Int {
return i*j*x*y*4096
}
//compiler does not complain here...
filterClosure = filter
}
//other if statements with different closure definitions follow...
if let closure = filterClosure {
let image = filterImage(closure)
}
}
Your closure is only initialized if the code enters your if block (i.e. if type == FilterType.MyFilter). In the other case it is left uninitialized.