extension SKPhysicsBody {
/// anchorPoint version of init(rectangleOfSize:center:)
convenience init(rectangleOfSize s: CGSize, withAnchorPoint anchorPoint: CGPoint) {
var center = CGPoint()
center.x = (s.width / 2) - ( s.width * anchorPoint.x)
center.y = (s.height / 2 ) - ( s.height * anchorPoint.y)
self.init(rectangleOfSize: s, center: center)
}
}
I got this error on runtime
-[PKPhysicsBody initWithRectangleOfSize:withAnchorPoint:]: unrecognized selector sent to instance 0x7f9b03c4fff0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PKPhysicsBody initWithRectangleOfSize:withAnchorPoint:]: unrecognized selector sent to instance 0x7f9b03c4fff0'
This is how I call in code
// redBox is a typical SKSpriteNode()
redBox.physicsBody = SKPhysicsBody(rectangleOfSize: redBox.frame.size, withAnchorPoint: redBox.anchorPoint)
I basically want to extend the SKPhysicsBody class to provide a convenience initializer to its factory method
As #Cristik guessed in the comments, this is the same root problem as this question and this question: the public SKPhysicsBody class is a convenience interface for the private PKPhysicsBody class that provides its implementation.
In the past, this approach relied heavily on the "duck typing" behavior of Objective-C — as long as ClassA responds to all the same selectors as ClassB, you can call any of those selectors on a pointer whose static type (the type declared to the compiler in source code) is ClassB, even if the actual object at run time is really an instance of ClassA.
Swift is stricter about runtime type correctness than ObjC, so "duck typing" alone is not enough. Since iOS 9 / OS X 10.11, SpriteKit has some workarounds that allow PKPhysicsBody instances to better pretend to be SKPhysicsBody instances.
But those don't cover all cases — in particular, it doesn't catch (ObjC) [SKPhysicsBody alloc] returning a PKPhysicsBody instance, which means any attempt to add initializers to SKPhysicsBody in Swift will fail. (Because the ObjC alloc/init process is reduced to one initializer call in Swift.)
I'd consider this a bug and recommend filing it with Apple.
Edit/update: And until that bug gets fixed (it's been a year and some now), the workaround is to make your convenience "initializer" into a class method instead. (Or a global function if you must, but... ewww.)
Related
I'm learning about delegates and I don't understand why
let friendsFunctionsDelegate = FriendsFunctionsDelegate()
let friendsFunctions = FriendsFunctions()
friendsFunctionsDelegate.delegate = friendsFunctions
is correct whilst
let friendsFunctionsDelegate = FriendsFunctionsDelegate()
friendsFunctionsDelegate.delegate = FriendsFunction()
is wrong.
Here is the full code:
protocol FriendsDelegate: AnyObject {
func orderPizza()
func takeABreak()
}
class FriendsFunctionsDelegate {
weak var delegate: FriendsDelegate? = nil
func buyPizza() {
delegate?.orderPizza()
}
func sleep() {
delegate?.takeABreak()
}
}
class FriendsFunctions: FriendsDelegate {
func orderPizza() {
print("I ordered a pizza")
}
func takeABreak() {
print("I'm going to sleep")
}
}
let friendsFunctionsDelegate = FriendsFunctionsDelegate()
let friendsFunctions = FriendsFunctions()
friendsFunctionsDelegate.delegate = friendsFunctions
ARC (for Automatic Reference Counting)
ARC is the system which manages automatically the memory allocation / deallocation for your objects.
The way it works is that as long as you'll have AT LEAST 1 strong reference to an object in your code it will remain in memory. Please note that by default any property definition not define as weak is implicitly strong.
When specifying a property as weak it won't increment the "holding reference count" by one.
The issue you are experiencing...
In your first example, when you first create a let constant you hold a strong reference to it, then you assign it to the weak variable.
You have then =>
1 weak reference to the object (stored on the friendsFunctionsDelegate.delegate)
1 strong reference to the object (the let constant let friendsFunctions = FriendsFunctions())
As a result, ARC is NOT deallocating the object (strong reference >= 1) => It is working ✅
In your second example, when you directly instantiate + assign the delegate to the weak variable WITHOUT creating a constant first.
You have then =>
1 weak reference to the object (stored on the friendsFunctionsDelegate.delegate)
0 strong reference to the object
As a result, ARC is deallocating (release from memory) the object (strong reference == 0) directly after the assignment => Not working ❌
Conclusion
As a conclusion, you need to keep a strong reference somewhere to that delegate object.
Weak usage in delegate pattern
When using delegation we use a weak property to prevent memory retain cycle. This is happening when you have one object (Object A) holding a strong reference to another object (Object B) which is also having a strong reference to the first object (Object A).
A => B STRONG
B => A STRONG
=> ⚠️RETAIN CYCLE⚠️
When you'll try to get rid of object A or B from memory strong reference count will still be 1, you'll then get a memory leak with memory filled with unused objects, which might lead to an usable app. The solution is to define one of those 2 references as weak (not both). When applying the delegate pattern, you would define the property holding the reference to the delegate as weak.
Few extra comments:
Be careful to the naming which might be misleading. The delegate keyword should be appended only to the protocol and not to the class name. If you remove it though you would have an overlap, which is a hint that the naming could have more appropriately defined.
When you define a property as Optional and you want it to be nil as default, you don't have to explicitly specify the = nil. Though you can still do it ;)
Best practices tell you that when you want a class to conform to a delegate / protocol, you should go with an extension instead of directly conforming at the class definition.
Delegate pattern should be a blind communication pattern, so your class should not be aware about out of scope functions. Then the naming of the delegate methods should be modified to something like func didBuyPizza() and func didTakeABreak()or similar.
The one with
let friendsFunctions = FriendsFunctions() // holds a strong reference
friendsFunctionsDelegate.delegate = friendsFunctions
Works as it holds a strong reference , while this
friendsFunctionsDelegate.delegate = FriendsFunction()
Don't work as both parts (lhs & rhs) are weak so no retaining happens ( delegate is a weak property )
Notice how the FriendsFunctionsDelegate.delegate property is a weak reference:
weak var delegate: FriendsDelegate? = nil
^^^^
If you did
friendsFunctionsDelegate.delegate = FriendsFunction()
You created a FriendsFunction object, and the only object that has a reference to it is friendsFunctionsDelegate, via its delegate property. But this is a weak reference. There is no strong reference to the new FriendsFunction object! As a result, it will be deallocated immediately after it is created.
You should see a warning here that says:
Instance will be immediately deallocated because property 'delegate' is 'weak'
On the other hand, if you put the newly created FriendsFunction object into a let constant first,
let friendsFunctions = FriendsFunctions()
That let constant, friendsFunctions will be a strong reference to the FriendsFunctions object, since it is not weak. And since there is at least one strong reference to it, the object will not be deallocated until all the strong references are gone.
For more info, see Automatic Reference Counting in the Swift guide.
Back in objective-C with ARC, this wasn't safe to do:
MyClass* someObject = ...
__weak MyClass* weakSomeObject = someObject;
doSomething(^{
[weakSomeObject someMethod];
});
Why? because simply calling a method doesn't cause ARC to retain the object, and thus the someObject instance might be released and dealloced in the middle of the execution of someMethod
Bringing this forward into swift, it translates as follows:
let someObject: MyClass = ...
doSomething { [weak someObject]
someObject?.someMethod()
}
My question is, what are the semantics of the ?. operator in swift regarding ARC, and is it safe to use with weak reference method calls?
I can imagine the swift compiler translating the above code into something like this:
let someObject: MyClass = ...
doSomething { [weak someObject]
if let tmp = someObject {
tmp.someMethod()
}
}
If it did that, then it would indeed be safe, as tmp is a strong reference and thus would cause ARC to retain the object across the duration of the call to someMethod
However, I could also imagine it translating into something without an ARC retain for performance reasons or whatever.
Does anyone know what it actually does, and is there a specification or document that makes this explicit?
Bullet points to get first:
closures capture context.
if let, guard etc only checks if optional contains actual value but doesn't capture it's context.
Now about ?. It's optional chaining, if object on which method is called is nil, it won't call method as well as fail full chain(if you follow up return value from method with another method call or else).
About weak and unowned references to used objects inside closure:
weak doesn't create strong reference to object, so it makes it optional because object may not be there when it gets called
unowned as well as weak doesn't create strong reference, however object won't be optional, so you must guarantee that closure won't outlive object it uses, because you will get crash referencing object that isn't there.
Hope it helps.
Short answer is yes, it is safe. Longer explanation and link follow:
Methods defined for a type in Swift are curried functions like this:
class MyClass {
func doSomething() {
// I can access self in here
}
}
print(type(of: MyClass.doSomething)) //(MyClass) -> () -> ()
In other words, the function defined on the type is curried such that it must first be passed a reference to what will be self, which is strongly captured in the returned partially applied function (()->())
When you call the method on an instance of MyClass (not the type itself), it automatically does the initial partial application of passing in that instance of self which is then captured strongly by the resulting closure that is actually invoked.
So the type of MyClass.doSomething is (MyClass) -> () -> (), and the type of MyClass().doSomething is () -> (), where the initial MyClass reference is already captured inside the () -> ().
Given the below code:
weak var test: MyClass? = MyClass()
test?.doSomething()
On line 2, a couple things happen. First, if test is non-nil, it will be passed to the the method implementation on the type MyClass and captured by the MyClass type's curried function (MyClass) -> () ->(). This captured reference is the "self" that can be used inside that instance method.
Then, the resulting partially applied function of type ()->() is invoked, and the strong reference / capture of self from above is released once the function executes.
So, the answer is then yes, it is safe to call instance methods on a weak reference, because the instance method actually strongly captures the instance up until and while it is executing.
This post discusses how instance methods in Swift are curried in more detail, and links to the initial dev forum source of the information.
https://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/
UPDATE:
Some additional interesting and in-depth articles on this mechanism:
http://rosslebeau.com/2016/sneaky-reference-cycles-swift-instance-methods
https://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/
I am trying to subclass MKPolyline. However the problem is, MKPolyline does not have any designated initializers. Here is what I tried to do:
import MapKit
class MyPolyline: MKPolyline {
let locations: [Location]
init(locations: [Location]) {
self.locations = locations
var coordinates: [CLLocationCoordinate2D] = []
for location in locations {
coordinates.append(location.coordinate)
}
super.init(coordinates: coordinates, count: coordinates.count)
}
}
But I am getting Must call a designated initializer of the superclass 'MKPolyline' error at the super.init call. To the best of my knowledge, MKPolyline does not have any designated initializers.
What should I do? How can I subclass a class which doesn't have any designated initializers?
Designated Initializers
As you seem to know, designated initializers provide a way of creating an object from a set of parameters. Every Swift object must have at least one designated initializer, although in certain cases Swift may provide them by default.
In the case of a class, a default initializer will be provided iff all stored properties of the class provide default values in their declarations. Otherwise, a designated initializer must be provided by the class creator. Even if a designated init is provided explicitly, however, it is not required to have an access control level that would make it accessible to you.
So MKPolyline surely has at least one designated init, but it may not be visible to you. This makes subclassing effectively impossible, however there are alternatives.
Two alternatives immediately come to mind:
Class Extensions and Convenience Initializers
One great feature of Swift is that classes may be extended even if the original class was defined in another module, even a third party one.
The issue you are running into is that MKPolyline defines convenience inits but no public designated ones. This is an issue because Swift has a rule that a designated init must call a designated init of its immediate superclass. Because of this rule, even a designated init will not work, even in an extension. Fortunately, Swift has convenience initializers.
Convenience inits are just initializers that work by eventually calling designated inits within the same class. They do not delegate up or down, but sideways, so to speak. Unlike a designated init, a convenience init may call either a designated init or another convenience init, as long as it is within the same class.
Using this knowledge, we could create an extension to MKPolyline that declares a convenience init which would call one of the other convenience inits. We can do this because inside of an extension it is just like you are in the original class itself, so this satisfies the same-class requirement of convenience inits.
Basically, you would just have an extension with a convenience init that would take an array of Location, convert them to coordinates, and pass them to the convenience init MKPolyline already defines.
If you still want to hold the array of locations as a stored property, we run into another problem because extensions may not declare stored properties. We can get around this by making locations a computed property that simply reads from the already-existing getCoordinates method.
Here's the code:
extension MKPolyline {
var locations: [Location] {
guard pointCount > 0 else { return [] }
let defaultCoordinate = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
var coordinates = [CLLocationCoordinate2D](repeating: defaultCoordinate, count: pointCount)
getCoordinates(&coordinates, range: NSRange(location: 0, length: pointCount))
// Assuming Location has an init that takes in a coordinate:
return coordinates.map({ Location(coordinate: $0) })
}
convenience init(locations: [Location]) {
let coordinates = locations.map({ $0.coordinate })
self.init(coordinates: coordinates, count: coordinates.count)
}
}
Here's what's going on. At the bottom we have a convenience init very similar to what you already did except it calls a convenience init on self since we're not in a subclass. I also used map as a simpler way of pulling the coordinates out of Location.
Lastly, we have a computed locations property that uses the getCoordinates method behind the scenes. The implementation I have provided may look odd, but it is necessary because the getCoordinates function is Objective-C–based and uses UnsafeMutablePointer when imported to Swift. You therefore need to first declare a mutable array of CLLocationCoordinate2D with exact length, and then pass it to getCoordinates, which will fill the passed array within the range specified by the range parameter. The & before the coordinates parameter tells Swift that it is an inout parameter and may be mutated by the function.
If, however, you need locations to be a stored property in order to accommodate a more complex Location object, you'll probably need to go with the second option described below, since extensions may not have stored properties.
Wrapper Class
This solution doesn't feel as 'Swifty' as the previous, but it is the only one I know of that would let you have a stored property. Basically, you would just define a new class that would hold an underlying MKPolyline instance:
class MyPolyline {
let underlyingPolyline: MKPolyline
let locations: [Location]
init(locations: [Location]) {
self.locations = locations
let coordinates = locations.map { $0.coordinate }
self.underlyingPolyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
}
}
The downside to this approach is that anytime you want to use MyPolyline as an MKPolyline, you will need to use myPolyline.underlyingPolyline to retrieve the instance. The only way around this that I know of is to use the method described by the accepted answer to this question in order to bridge your type to MKPolyline, but this uses a protocol that is undocumented and therefore may not be accepted by Apple.
I searched for hours on SO (and on other sites) about this problem, but finally I could rewrite a more generic solution. Thanks for the original author :)
In Swift 2.3, we can write something like this:
var rect = CGRect(...)
rect.offsetInPlace(dx: 15, dy: 0)
to move a rect 15pt to the right.
However in Swift 3, it seems like this function does no longer exist.
When inspecting the CGRect interface we can only see the non mutating variant offsetBy(dx:, dy:). This is also true in all the places we've usually used mutating functions (named ...inPlace). I have already searched the Swift evolution repo on GitHub, but couldn't find any notes about this.
Have the mutating variants been removed? Is the remaining function automatically mutating depending on whether or not the return value is used? IMHO, it would be a shame, if they have actually been removed, because they used to be very convenient when doing code based layout, etc.
You can make one for compatibility.
extension CGRect {
mutating func offsetInPlace(dx: CGFloat, dy: CGFloat) {
self = self.offsetBy(dx: dx, dy: dy)
}
}
It seems there is no offsetInPlace anymore. Please see the screen here is all available functions in Instance Methods
Does anybody know why I get BAD_ACCESS on getting & setting of my iVars with the following code ?
class myClass: NSObject {
var model = "Unspecified"
override init() {
super.init()
var key: NSString = "model"
var aClass : AnyClass? = self
var ivar: Ivar = class_getInstanceVariable(aClass, key.UTF8String)
// Set
object_setIvar(aClass, ivar, "R56")
// Get
var value: AnyObject = object_getIvar(aClass, ivar)
}
}
myClass()
You get a bad access because Swift classes do not have traditional iVars anymore (try to grab a Swift class' iVar layout and see for yourself). But Swift classes are also Objective-C objects to the runtime, and there don't appear to be any paths excluding Swift classes in it.
What winds up happening is the runtime hands you a bogus iVar that it truly thinks points to a proper offset in the class definition (probably 0 or thereabouts because it doesn't exist). When you try to get said iVar, the runtime literally derefs you some of the object's bytes and tries to wrap it back up in an Objective-C pointer. Often this data has the tagged bit set unintentionally, and so often maps to tagged classes (in my tests I was reliably getting back a tagged pointer for what the runtime thought was NSMutableData).
Long story short: you can't do it anymore.