Since being an OS X/iOS Developer for years I have a strong interest in Apples Swift language. In order to keep my initialization methods clean and simple I am isolating individual parts of the initialization into individual methods. Especially when dealing with long initializations - like often found in a SpriteKit SKScene subclass this helps me keeping a clean and simple structure. This is how it looks like:
Typical Objective-C Class:
#interface SomeScene ()
#property (strong, nonatomic) SKSpriteNode* backgroundNode;
#property (strong, nonatomic) SKNode* hudNode;
#property (strong, nonatomic) SKSpriteNode* playerNode;
#end
#implementation SomeScene
-(id)init {
if (self = [super init]) {
[self setupBackgroundNode];
[self setupHUD];
[self setupPlayer];
}
return self;
}
-(void)setupBackgroundNode {
self.backgroundNode = [SKSpriteNode new];
self.backgroundNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.backgroundNode];
}
-(void)setupHUD {
self.hudNode = [SKSpriteNode new];
self.hudNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.hudNode];
}
-(void)setupPlayer {
self.playerNode = [SKSpriteNode new];
self.playerNode.position = CGPointMake(0., 0.);
/* Some More Options */
[self addChild:self.playerNode];
}
#end
This is just a demonstration to show you how I encapsulate different parts of the initialization into individual methods which are then all being called at the time the -(id)init is being called. I am using this kind of pattern in any of my projects.
Now with learning Swift I read about the basic concepts, about optionals and tried to get used to the slightly different type of initialization. And in order to keep my own Objective-C pattern shown above the Swift code will look like this:
Same class in Swift:
class SomeScene: SKScene {
var backgroundNode: SKSpriteNode?
var playerNode: SKSpriteNode?
var hudNode: SKSpriteNode?
override convenience init() {
self.init()
self.setupBackground()
self.setupPlayer()
self.setupHUD()
}
func setupBackground() {
backgroundNode = SKSpriteNode(/*Some Initialisation Parameters*/)
backgroundNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(backgroundNode!);
}
func setupPlayer() {
playerNode = SKSpriteNode(/*Some Initialisation Parameters*/)
playerNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(playerNode!);
}
func setupHUD() {
hudNode = SKSpriteNode(/*Some Initialisation Parameters*/)
hudNode?.position = CGPointMake(0.0, 0.0)
/* even more lines */
self.addChild(hudNode!);
}
}
This does work like expected but I was asking myself if this is the correct way of using optionals. I am afraid that I am misusing them just to convert an Objective-C style to Swift. Do I really have to write all that question marks after every property? Does that something or is it more of a way to switch of a compiler-error that this property "might be nil" - which is what Apples Documentation says about the ?-suffix.
I came up to this question because using swift that way feels a bit strange. Is there a better way to encapsulate code-parts / blocks from an init method in order to prevent it from growing and growing and loosing focus ?
Do I really have to write all that question marks after every property?
Basically, yes. Swift has strong rules about initializers. You must initialize all instance properties by the end of initialization. If you are going to move initialization of instance properties out of the initializer, then you must provide a default initial value for those instance properties in some other way. You can do that with an equal sign and an explicit value, or you can do it with an Optional because it automatically assigns nil.
That is why outlets (#IBOutlet) are usually Optionals. We know they won't be initialized during initialization, so we need a temporary value until they are initialized when the nib loads.
Also it is common practice to use Optionals for any instance property that simply cannot be initialized until after the instance itself has been fully initialized. For example, you might have some time-consuming operation to perform, and you don't want to do that in the initializer.
However, there is another approach that might be appropriate for some of your instance properties: mark them as lazy and provide a default initializer that is a define-and-call function. For example:
lazy var prog : UIProgressView = {
let p = UIProgressView(progressViewStyle: .Default)
p.alpha = 0.7
p.trackTintColor = UIColor.clearColor()
p.progressTintColor = UIColor.blackColor()
p.frame = CGRectMake(0, 0, self.view.bounds.size.width, 20)
p.progress = 1.0
return p
}()
That has the advantage of encapsulation. We have provided a default value, namely the thing returned from the define-and-call function, so we don't have to initialize in an initializer. But the compiler allows us to postpone running the define-and-call function until the first time some other code actually accesses this instance property. That pattern might make more sense for you.
Also I find myself using computer properties a lot. That's another good alternative pattern in many cases.
So basically yes, what you are doing is right, but Swift has some other patterns that can often make it unnecessary, and when you're coming from Objective-C you'll want to get comfortable with those patterns too.
Related
I'm probably missing something here as this seems like a pretty basic Objective-C question, but not finding any similar questions (maybe I'm searching the wrong thing, sorry in advance if so!). My question is around enforcing a class that conforms to a protocol to have the correct type in it's method signatures. Since that doesn't seem to be enforced by the compiler, what's the best practice here.
Let me provide an example.
// View.h
#protocol View <NSObject>
- (void)createNewViewUsing:(UIView *)view;
#end
// SomeView.h
#interface SomeView : NSObject <View>
#end
- (void)createNewViewUsing:(UIStackView *)view {
NSLog(#"The view is %#", view);
NSLog(#"%#", view.arrangedSubviews); // CRASH!
}
// Implementation
SomeView *const view = [[SomeView alloc] init];
UIView *const anotherView = [[UIView alloc] init];
[view createNewViewUsing:anotherView];
How do I avoid this crash? Is the correct thing to do to just make sure I'm always matching the method signature as my protocol and then check the type? How do I ensure someone else (or my future self) does this? Why does the compiler not enforce this?
In Objective-C you have to keep track of it yourself. So you are totally allowed to do
- (void)createNewViewUsing:(UIStackView *)view
with the understanding that you will then only message this with stackviews and not with views. If you deviate from this you do so at your own risk.
However, a better approach is to stick to the original protocol with something as below
- (void)createNewViewUsing:(UIView *)view {
if ( [view isKindOfClass:UIStackView.class] )
{
// Cast it
UIStackView * sv = view;
// Now you are safe ... use sv in what follows
}
else // ... not a stackview ... maybe pass to parent class
This of course adds a lot of overhead to your code but you may totally need it for e.g. a large project. You are the one making the judgement call here.
I'm trying to learn how to use UIViewPropertyAnimator in objc. I made a simple test app with an object called 'blueBox'. I want to vary the properties of blueBox.
I declare 'animator' outside of #implementation ... #end:
UIViewPropertyAnimator *animator;
then define it like so:
- (void)viewDidLoad {
[super viewDidLoad];
CGRect newFrame = CGRectMake(150.0, 350.0, 100.0, 150.0);
animator = [[UIViewPropertyAnimator alloc]
initWithDuration:2.0
curve:UIViewAnimationCurveLinear
animations:^(void){
self.blueBox.frame = newFrame;
self.blueBox.backgroundColor = [UIColor redColor];
}];
}
When I want to use it I write:
animator.startAnimation;
It works as expected (changes the object's color and frame) but there is a warning on 'animator.startAnimation;' that says "Property access result unused - getters should not be used for side effects". What property access result is that referring to? How should I write that so I don't get a warning?
startAnimation is a method, not a property. You should write:
[animator startAnimation];
Though Objective-C does allow you to use property syntax when calling a method that takes no parameters, your use is written like you are attempting to read a property value. But since (obviously) you make no attempt to store the result (there isn't one), the compiler complains you are ignoring the accessed value.
Simply avoid the wrong syntax and you avoid the issue.
BTW, you claim that the line:
UIViewPropertyAnimator *animator;
is outside the #implementation / #end pair. That makes it a file global variable. Is that what you really want? If you want it to be an instance variable of the class (which is probably what you really want), it should be:
#implementation YourClass {
UIViewPropertyAnimator *animator; //instance variable
}
// your methods
#end
Well I'm just confused when the lazy instantiation should be used.
I understand the basic concept of lazy instantiation though.
" I understand that all properties start out as nil in Objective-C and that sending a message to nil does nothing, therefore you must initialize using [[Class alloc] init]; before sending a message to a newly created property. "(Lazy instantiation in Objective-C/ iPhone development)
m.file:
#property (strong, nonatomic) NSMutableArray *cards;
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
} }
Well, what I really don't get is when I'm supposed to use this type of instantiation?
Mostly I see the code like this:
h.file:
#interface Card : NSObject
#property (strong, nonatomic) NSString *contents;
m.file:
if([card.contents isEqualToString:self.contents]){
score = 1;
}
*This might be a stupid question but I'm really confused. I'm new here, Thanks.
There is no reason to use Lazy Instantiation/Lazy Initialization if you find it confusing; simply initialize your instance variables/properties in the class init methods and don't worry about it.
As the object is created as a side-effect of calling the getter method, it's not immediately obvious that it is being created at all, so one alternative, which would also mean you can use the default compiler-generate getter method, is to explicitly check for it in addCard:
- (void)addCard:(Card *)card
atTop:(BOOL)atTop
{
if (!self.cards)
self.cards = [NSMutableArray new];
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
(and removing the user-supplied getter method)
However the net-effect is the same as the code you posted, with the exception that self.cards will return nil until addCard is called, however I doubt this will cause a problem.
When using dot notation to access your instance variables, you are calling your getter method for that given property. Therefore, by using dot notation and lazy instantiation, your getter will always assert that a property is not nil before you send it a message. Therefore, code such as
[self.cards insertObject:card atIndex:0];
will actually call the getter at self.cards; if you use dot notation on your objects and program the getters accordingly, you will always ensure that your instance variables are allocated and initialized, while simultaneously cleaning up your init method for code that is much more important.
Lazy instantiation is a common practice among Objective-C programmers; I suggest getting into the flow of the convention.
EDIT: thanks for Raphael mentioning this in a comment previously.
Lazy instantiation is a performance enhancement in certain types of scenarios. One example would be a class that has a very expensive user facing UI string.
If you create many of instances of that class but only a very small subset of those instances will be shown in your UI, you waste a lot of CPU resources creating a very expensive UI string that rarely will be used.
I have a MKPolyline subblass which I want to implement NSCoding, i.e.
#interface RSRoutePolyline : MKPolyline <NSCoding>
I asked a question on the best way to encode the c-array and got an excellent answer. However, there is no init method defined on MKPolyline, i.e. there is no other way to give it data other than its class method polylineWithPoints:points.
Is this code where my comment is ok?
- (void)encodeWithCoder:(NSCoder *)aCoder
{
MKMapPoint *points = self.points;
NSUInteger pointCount = self.pointCount;
NSData *pointData = [NSData dataWithBytes:points length:pointCount * sizeof(MKMapPoint)];
[aCoder encodeObject:pointData forKey:#"points"];
[aCoder encodeInteger:pointCount forKey:#"pointCount"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSData* pointData = [aDecoder decodeObjectForKey:#"points"];
NSUInteger pointCount = [aDecoder decodeIntegerForKey:#"pointCount"];
// Edit here from #ughoavgfhw's comment
MKMapPoint* points = (MKMapPoint*)[pointData bytes];
// Is this line ok?
self = (RSRoutePolyline*)[MKPolyline polylineWithPoints:points count:pointCount];
return self;
}
You should call an init method on any subclass of NSObject. Since MKPolyline is an NSObject, you should init it.
But MKPolyline has no methods and no init. This is Objective C's was of telling you that you can't subclass it.
Instead, as WDUK suggested, define your own class. It keeps track of your list point points, and manages NSCoding to save and restore them as needed.
#interface RSPolyline: NSObject<NSCoding>
- (id) initWithPoints: (NSArray*) points;
- (id) initWithCoder:(NSCoder *)aDecoder;
- (void) encodeWithCoder:(NSCoder *)aCoder;
- (MKPolyline*) polyLine;
#end
Your class can generate a polyline on request, perhaps caching the result if performance is an issue.
As a rule, don't reach for inheritance first. When you want to extend and improve a class, think first of composition.
It's dirty not to call [super init], and it doesn't bode well with my idea of good programming. Without calling super yourself, it isn't a true subclass; just a bastardization of composition that relies on a side effect of calling a convenience constructor. Saying this, I believe your method described will work OK, but it goes against the grain of good Objective-C programming and its conventions.
What I would suggest is to use MKPolyLine as an MKPolyLine instance, and use a category to add the extra bells and whistles you need. As for adding extra instance variables and such, you can use associated objects. An introduction to this concept can be found here, and this SO question addresses the use of them with categories: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?
While it is generally allowed to create and return a different object in an init method, there are three problems with that line (explained below). Instead of this, I would suggest overriding the points and pointCount properties so that you can return values stored in an instance variable, and call the super implementation there if the instance variable is empty. Then, your initializer just sets these instance variables so that they will be used.
- (MKMapPoint *)points {
if(myPointsIvar == NULL) return [super points];
else return myPointsIvar;
}
// similarly for pointCount
The first problem is that you are creating a new object, but not releasing the old one, which means you are leaking it. You should store the result in a different variable, then release self, then return the result (you don't need to store it in self).
Second, polylineWithPoints:count: returns an autoreleased object, but initWithCoder: should return a retained one. Unless there is another retain on it, it could be deallocated while you are still using it.
If these were the only problems, you could solve both like this:
MKPolyline *result = [MKPolyline polylineWithPoints:points count:pointCount];
[self release];
return [result retain];
However, there is a third problem which cannot be solved so easily. polylineWithPoints:count: does not return a RSRoutePolyline object, and the object it returns may not be compatible with your subclass's methods (e.g. it probably won't support NSCoding). There really isn't a way to fix this, so you can't use polylineWithPoints:count:.
Could someone share some knowledge on whats best practice / code convention on using #property iVars in init methods or designated initializers?
please see my example:
#interface MyClass ()
#property(nonatomic,strong) nsstring *tempString;
#property(nonatomic,strong) NSMutableArray *arrItems;
#end
#implementation ViewController
- (id)init
{
if (self = [super init]) {
//Is this best practice / correct
_tempString = #"";
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
...
...
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
#end
Any advice on why one or the other should be used?
Thanks...
Apple's guidance on this topic is included in the aptly named section Don’t Use Accessor Methods in Initializer Methods and dealloc.
Read this thread: Why shouldn't I use Objective C 2.0 accessors in init/dealloc?
In other words if you are not goiung to use KVO you can use second approach:
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
But be care full with alloc-init, don't forget about autorelease.
It's typically better to use property notation when you define it, partly(mostly?) for the reason Jeremy mentioned.
Debugging a particular variable is a whole lot easier when you can set a breakpoint in method setter override and have it apply to ALL code paths that modify the variable.
Another reason is to keep a consistent memory management model, although it is less important since you are using ARC. If you weren't however, and strong was retain, then you would make sure that the object you are setting to the property is autoreleased everywhere you set the property, and not have to deal with releasing the current value if you are directly setting the variable.
Consistency is important for maintenance/readability and debugging, no matter what practices you use.
I prefer the lazy instantiation method for properties.
After you #synthesize you can override your getter to lazily instantiate your property
For Example:
-(NSString *)tempString {
if(!tempString) {
_tempString = #"";
}
return _tempString;
}
and
-(NSMutableArray *)arrItems {
if(!_arrItems) {
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return _arrItems;
}
If you do want to set your property in the init method, use dot notation self.myProperty so that it uses the defined setter for the property and not the private class method directly.
According to Apple, you should not use accessors in init... or dealloc methods:
You should always access the instance variables directly from within
an initialization method because at the time a property is set, the
rest of the object may not yet be completely initialized. Even if you
don’t provide custom accessor methods or know of any side effects from
within your own class, a future subclass may very well override the
behavior.
Taken from this doc: Encapsulating Data.