I'm trying to get alloc init and nil to work within a method in ObjC - objective-c

should hopefully be an easy question (teaching myself Objective C as I go).
I would like to send a method to an object (Cell) which returns a different object (Waste), and then deletes the reciever (Cell). Here is what I'm looking at so far:
#import "Cell.h"
#import "Waste.h"
#implementation Cell
#synthesize.......
other methods.....
// Send a method to an instance of class "Cell", causing a new object of
// class "Waste" to be made, then causing the Cell instance to "die"
- (Waste *) die {
// Create a new object, "newWaste", of class Waste
// ARC Semantic Issue: No known class method for selector 'alloc'
Waste *newWaste = [[Waste alloc] init];
// Set the energy of "newWa" to 10% what the Cell's energy is
newWaste.wasteEnergy = (0.1 * cellEnergy);
// Set the X coordinate of "r" to the Cell's X coordinate
newWaste.wasteXCoordinate = cellXCoordinate;
// Set the Y coordinate of "r" to the Cell's Y coordinate
newWaste.wasteYCoordinate = cellYCoordinate;
// Variable saying if the Waste is to be excreted set to "NO"
newWaste.wasteExcreted = NO;
// Return the new waste object
return newWaste;
// Have the Cell "die" ARC Semantic Issue:
// Cannot assign to 'self' outside of a method in the init family
self = nil;
}
I've put the two issues that come up behind the comments so you know where the problems are, but they're:
ARC Semantic Issue: No known class method for selector 'alloc'
and
ARC Semantic Issue: Cannot assign to 'self' outside of a method in the init family
Could someone tell me why this is happening and how to fix it? I had thought it was straightforward to do:
Class *newInstance = [[Class alloc] init];
and that this could be done inside a method so you can return the instance. And I had read that self = nil; was the normal way for releasing objects with ARC.

Related

Cocoa class member variable allocated inside function call nil unless forced to init/load

I come from a C/C++ background and am currently learning a bit about Cocoa and Objective-C.
I have a weird behavior involving lazy initialization (unless I'm mistaken) and feel like I'm missing something very basic.
Setup:
Xcode 10.1 (10B61)
macOS High Sierra 10.13.6
started from a scratch Cocoa project
uses Storyboard
add files TestMainView.m/.h
under the View Controller in main.storyboard, set the NSView custom class as TestMainView
tested under debug and release builds
Basically, I create an NSTextView inside a view controller to be able to write some text.
In TestMainView.m, I create the chain of objects programmatically as decribed here
There are two paths:
first one is enabled by setting USE_FUNCTION_CALL to 0, it makes the entire code run inside awakeFromNib().
second path is enabled by setting USE_FUNCTION_CALL to 1. It makes the text container and text view to be allocated from the function call addNewPage() and returns the text container for further usage.
First code path works just as expected: I can write some text.
However second code path just doesn't work because upon return, textContainer.textView is nil (textContainer value itself is totally fine).
What's more troubling though (and this is where I suspect lazy init to be the culprit) is that if I "force" the textContainer.textView value while inside the function call, then everything works just fine. You can try this by setting FORCE_VALUE_LOAD to 1.
It doesn't have to be an if(), it works with NSLog() as well. It even works if you set a breakpoint at the return line and use the debugger to print the value ("p textContainer.textView")
So my questions are:
is this related to lazy initialization ?
is that a bug ? is there a workaround ?
am I thinking about Cocoa/ObjC programming the wrong way ?
I really hope I am missing something here because I cannot be expected to randomly check variables here and there inside Cocoa classes, hoping that they would not turn nil. It even fails silently (no error message, nothing).
TestMainView.m
#import "TestMainView.h"
#define USE_FUNCTION_CALL 1
#define FORCE_VALUE_LOAD 0
#implementation TestMainView
NSTextStorage* m_mainStorage;
- (void)awakeFromNib
{
[super awakeFromNib];
m_mainStorage = [NSTextStorage new];
NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
#if USE_FUNCTION_CALL == 1
NSTextContainer* textContainer = [self addNewPage:self.bounds];
#else
NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(FLT_MAX, FLT_MAX)];
NSTextView* textView = [[NSTextView alloc] initWithFrame:self.bounds textContainer:textContainer];
#endif
[layoutManager addTextContainer:textContainer];
[m_mainStorage addLayoutManager:layoutManager];
// textContainer.textView is nil unless forced inside function call
[self addSubview:textContainer.textView];
}
#if USE_FUNCTION_CALL == 1
- (NSTextContainer*)addNewPage:(NSRect)containerFrame
{
NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(FLT_MAX, FLT_MAX)];
NSTextView* textView = [[NSTextView alloc] initWithFrame:containerFrame textContainer:textContainer];
[textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
#if FORCE_VALUE_LOAD == 1
// Lazy init ? textContainer.textView is nil unless we force it
if (textContainer.textView)
{
}
#endif
return textContainer;
}
#endif
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
#end
TestMainView.h
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
#interface TestMainView : NSView
#end
NS_ASSUME_NONNULL_END
I am not familiar with cocoa that much but I think the problem is ARC (Automatic reference counting).
NSTextView* textView = [[NSTextView alloc] initWithFrame:containerFrame textContainer:textContainer];
In the .h file of NSTextContainer you can see NSTextView is a weak reference type.
So after returning from the function it gets deallocated
But if you make the textView an instance variable of TestMainView it works as expected.
Not really sure why it also works if you force it though. ~~(Maybe compiler optimisation?)~~
It seems forcing i.e calling
if (textContainer.textView) {
is triggering retain/autorelease calls so until the next autorelease drain call, textview is still alive.(I am guessing it does not get drained until awakeFromNib function returns). The reason why it works is that you are adding the textView to the view hierarchy(a strong reference) before autorelease pool releases it.
cekisakurek's answer is correct. Objects are deallocated if there is no owning (/"strong") reference to them. Neither the text container nor the text view have owning references to each other. The container has a weak reference to the view, which means that it's set to nil automatically when the view dies. (The view has an non-nilling reference to the container, which means you will have a dangling pointer in textView.textContainer if the container is deallocated while the view is still alive.)
The text container is kept alive because it's returned from the method and assigned to a variable, which creates an owning reference as long as that variable is in scope. The view's only owning reference was inside the addNewPage: method, so it does not outlive that scope.
The "force load" has nothing to do with lazy initialization; as bbum commented, that it "works" is most likely to be accidental. I strongly suspect it wouldn't in an optimized build.
Let me assure you that you do not need to go around poking properties willy-nilly in Cocoa programming. But you do need to consider ownership relations between your objects. In this case, something else needs to own both container and view. That can be your class here, via an ivar/property, or another object that's appropriate given the NSText{Whatever} API (which is not familiar to me).

How to switch two objects in objective-c

I am trying to find if collision occurs between two rectangles in objective-c. I thought one way to accomplish this would be detect the rectangle that is closest to 0,0 point then do rest of the work.
I wrote a function that takes two rectangle objects as parameters and does the math to calculate area, distance to origin etc....
So lets say rect1 is at (100,200) and rect1's width is 100 height 200, rect2 is at 150,150 and rect2's width is 100 height 200 this is calculated by function well enough.
If I switch rec1 and rect2 properties, so rect1 will be at 150,150 while rect2 will be at 100,200. And call following function
-(Rectangle*)intersect:(Rectangle*)rectA:(Rectangle*)rectB{
//check closest rectangle to 0,0 and switch rectangles
if (rectA.origin.x>rectB.origin.x) {
Rectangle *temporary = [[Rectangle alloc] init];
temporary=rectA;
rectA=rectB;
rectB=temporary;
[temporary release];
}
float rectAX = rectA.origin.x;
float rectAY = rectA.origin.y;
float rectBX = rectB.origin.x;
float rectBY = rectB.origin.y;
When I enable guard malloc and zombies I get following error:
-[Rectangle origin]: message sent to deallocated instance 0x100acffd0
As soon as rectA.origin.x; is called I get the error.
So Howcome rectA or rectB is deallocated? What is the correct way to switch two objects that has bunch of properties ?
There is a built in function for comparing CGRects CGRectIntersectsRect(rectA, rectB) that you can use to check your rectangle's frames :)
As far as your code for switching you have created a third object by allocing temporary. Then you set the temporary pointer at rectA and then you release rectA at the end since its pointing to temporary. Leaving the newly created object as a leak and then sending messages to the released rectA.
You don't really want to swap object pointers like that if you can help it in my experience. But if you absolutely have to and understand what's going on you could do it like this:
// Create copies of your objects
Rectangle *rectACopy = [rectA copy];
Rectangle *rectBCopy = [rectB copy];
// release the originals.
[rectA release];
[rectB release];
// Set your copies to the original pointers.
rectA = rectBCopy;
rectB = rectACopy;
NSCopying Protocol
First you need to implement the protocol.
#interface Rectangle : NSObject <NSCopying>
Then you need to create the new method. This will create a new object but with all the same values.
- (id)copyWithZone:(NSZone *)zone
{
id copy = [[[self class] alloc] init];
if (copy) {
// Copy NSObject based properties like UIViews, NSArrays, Subclassed objects.
[copy setObjectProperty:[self.objectProperty copy]];
// Set primitives like ints, CGRects, Bools.
[copy setPrimitiveProperty:self.primitiveProperty];
}
return copy;
}
You don't need to allocate a new object instance for temporary (and therefore you don't need to release it either). You are just taking your 2 existing pointers and switching them around. You're correct to use a 3rd variable (temporary) but you don't need to allocate any new space because you're not moving anything in memory, just swapping which variables point to the existing objects.
-(Rectangle*)intersect:(Rectangle*)rectA:(Rectangle*)rectB{
//check closest rectangle to 0,0 and switch rectangles
if (rectA.origin.x>rectB.origin.x) {
//Rectangle *temporary = [[Rectangle alloc] init]; // here you don't need to allocate as you are not using this object
// So use
Rectangle *temporary=rectA;
rectA=rectB;
rectB=temporary;
//[temporary release]; //you don't need this. Here you were releasing rectA not the temp rect that you allocated just below if, as you assign rectA to temporary
}
float rectAX = rectA.origin.x;
float rectAY = rectA.origin.y;
float rectBX = rectB.origin.x;
float rectBY = rectB.origin.y;

Checking If GKScore Instances Have Context Property

For the online mode for my game, I am using the context property of GKScore, and as all devices which support Game Center can update to iOS 5 (which is when the context property was added), I am requiring that the context property is available to play online. However, I am having issues implementing this runtime-check. I was assuming that I could use [GKScore instancesRespondToSelector:#selector(setContext:)] to check its existence, but this returns false on the iOS 5 and 5.1 simulators, as well as for #selector(context). Why on earth is this happening, and what is the cleanest and correct way to perform this check, please?
This looks like a bug in the GK implementation.
Consider the following code...
// Get the C-functions that are really called when the selector message is sent...
typedef BOOL (*XX)(id, SEL, SEL);
XX classImpNSObject = (XX)[NSObject
methodForSelector:#selector(instancesRespondToSelector:)];
XX classImpGKScore = (XX)[GKScore
methodForSelector:#selector(instancesRespondToSelector:)];
XX instImpNSObject = (XX)[NSObject
instanceMethodForSelector:#selector(respondsToSelector:)];
XX instImpGKScore = (XX)[GKScore
instanceMethodForSelector:#selector(respondsToSelector:)];
// See that the same C function is called for both of these...
NSLog(#"instancesRespondToSelector: %p, %p", classImpNSObject, classImpGKScore);
// But, different functions are called for these...
NSLog(#"respondsToSelector: %p, %p", instImpNSObject, instImpGKScore);
// Invoke to C-Functions for instancesRespondToSelector:
NSLog(#"NSObject instancesRespondToSelector: context: %s",
classImpNSObject(
[NSObject class],
#selector(instancesRespondToSelector:),
#selector(context))
? "YES" : "NO");
NSLog(#"GKScore instancesRespondToSelector: context: %s",
classImpGKScore(
[GKScore class],
#selector(instancesRespondToSelector:),
#selector(context))
? "YES" : "NO");
// Invoke the C functions for respondsToSelector:
GKScore *gkScore = [[GKScore alloc] init];
NSLog(#"NSObject respondsToSelector: context: %s",
instImpNSObject(
gkScore,
#selector(respondsToSelector:),
#selector(context))
? "YES" : "NO");
NSLog(#"GKScore respondsToSelector: context: %s",
instImpGKScore(
gkScore,
#selector(respondsToSelector:),
#selector(context))
? "YES" : "NO");
Basically, we just extracted the C functions that get called when responding to those messages.
As you can see, NSObject and GKScore use the exact same C-function implementation for instancesRespondToSelector:. However, they use different C-function implementations for respondsToSelector:. This means that GKScore overrides respondsToSelector: with its own implementation (but does not override instancesRespondToSelector.
If you send the same GKScore instance to the different C implementations of respondsToSelector: you get different results for some selectors (obviously, or there would not be a reason to provide a subclass implementation).
It looks like they did something funky for a few special properties, and provided an override for respondsToSelector: to handle the special cases, but forgot about making sure instancesRespondToSelector: did the right thing.
If you want to troll through assembly code, set a breakpoint and I'm sure you can see the differences.
I did not do that.
My own personal curiosity will only carry me so far :-)
For you situation, of trying to detect the method implementation in code, I suggest creating a temporary GKScore object to do your tests, cache that result, and free the temporary object.
I can't fully explain this, but an instantiated object of class GKScore will return YES to repondsToSelector(context), even while the class says it won't. If no other solution works, construct a GKScore object just to query it.
I wondered if [[GKScore alloc] init] actually returns an object with type other than GKScore. This can happen.
GKScore *instantiatedScore = [[GKScore alloc] init]; // Add autorelease if using manual reference counting.
NSString* className = NSStringFromClass([instantiatedScore class]);
NSLog(#"instantiatedScore class name = %#", className);
But, it doesn't, according to this output:
instantiatedScore class name = GKScore
I wondered if the compiler directives in the GKSCore.h header file might affect this. It defines two properties that are only available in iOS 5.0 or greater: context and shouldSetDefaultLeaderboard. Maybe those compiler directives mean that the class can't guarantee it will support those two properties.
Under this hypothesis [GKScore instancesRepondToSelector:#selector(category)] should return YES, but [GKScore instancesRepondToSelector:#selector(shouldSetDefaultLeaderboard)] should return NO.
GKScore *instantiatedScore = [[GKScore alloc] init]; // Add autorelease if using manual reference counting.
NSLog(#"GKScore category = %d", [GKScore instancesRespondToSelector:#selector(category)]);
NSLog(#"instantiatedScore category = %d", [instantiatedScore respondsToSelector:#selector(category)]);
NSLog(#"GKScore context = %d", [GKScore instancesRespondToSelector:#selector(context)]);
NSLog(#"instantiatedScore context = %d", [instantiatedScore respondsToSelector:#selector(context)]);
NSLog(#"GKScore shouldSetDefaultLeaderboard = %d", [GKScore instancesRespondToSelector:#selector(shouldSetDefaultLeaderboard)]);
NSLog(#"instantiatedScore shouldSetDefaultLeaderboard = %d", [instantiatedScore respondsToSelector:#selector(shouldSetDefaultLeaderboard)]);
But, the output is weirder than that:
GKScore category = 0
instantiatedScore category = 1
GKScore context = 0
instantiatedScore context = 1
GKScore shouldSetDefaultLeaderboard = 1
instantiatedScore shouldSetDefaultLeaderboard = 1
If you're specifically looking for the existence of a property, you should use the Objective-C runtime function:
class_getProperty(Class cls, const char *name)
To use it, you will have to import :
#import <objc/runtime.h>
As a tiny test example, here is how you could test for the existence of a particular property:
#import <objc/runtime.h>
//...
objc_property_t realP = class_getProperty([GKScore class], "context");
objc_property_t fakeP = class_getProperty([GKScore class], "fakeContext");
if (realP) {
NSLog(#"context exists");
}
if (!fakeP) {
NSLog(#"fakeContext does not exist");
}
// Both statements will log correctly.
As to why GKScore instances do not appear to respond to the correct selector, my thought would be that the context property may be declared #dynamic and thus +instancesRespondToSelector: and -respondsToSelector: would return NO (see this question). Not knowing the internal details, this is all I can suggest, but if you merely want to test the existence of a property, the sample code above will work.
Incidentally, if you don't want an include to the Objective-C runtime floating around, you may want to encapsulate this behaviour in a class or wrap it in a selector rather than just stick it in somewhere verbatim. That's entirely up to you of course.
I have also come across this issue but in my case with GKTurnBasedMatchParticipant. I did a quick dump of the result of sending #instancesRespondToSelector: to each of the properties of this class.
Here's the result:
1 playerID false
2 lastTurnDate false
3 status true
4 matchOutcome false
5 matchOutcomeString true
6 isWinner true
7 invitedBy false
8 inviteMessage false
9 internal true
Notice how many of the properties report that they can't be sent as selectors. However, notice also an additional "internal" property. Now look at the result of querying whether this internal object will respond to the property selectors:
1 playerID true
2 lastTurnDate true
3 status true
4 matchOutcome true
5 matchOutcomeString false
6 isWinner false
7 invitedBy true
8 inviteMessage true
9 internal false
Hence, many of the missing properties are in here. I guess that it's not really safe to make use of a non-documented "internal" feature to get around an apparent Apple bug but it's still interesting to know nonetheless.
EDIT: After another day's mucking around I have found the issue here. These rogue properties are actually set up as forwarding methods to forward to the "internal" object. Being an ObjectiveC noob, I hadn't realized that this is a perfectly acceptable thing to do.
In my case, I'm not just trying to detect if an object responds to a selector but I actually want to invoke it too. Hence the general solution to cope with forwarding is:
(a) To check availability of a response use [instance #respondsToSelector: sel] rather than [[instance class] instanceRespondsToSelector: del].
(b) To invoke a method that may, or may not, be forwarded do this:
NSMethodSignature *signature = [instance methodSignatureForSelector:sel];
if (!signature) {
// It's possible this is a message forwarding selector, so try this before giving up.
NSObject *fwd=[instance forwardingTargetForSelector:sel];
if (fwd && (signature= [fwd methodSignatureForSelector:sel]))
// Redirect to the forwarding target
instance=fwd;
else {
// ERROR case - selector is really not supported
}
}
NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:signature];
// Proceed with invocation setup
I hope this is useful to prevent others from wasting as much time as I have on this.

Initializing instance object in designated initializer?

I have a Rectangle class which has properties width and height. It also has an instance property/object called origin (-(XYPoint *) origin ). Below is the code for my designated initializer in which I pass the XYPoint object as an argument. Is there a way (or is it okay) if I take the properties of the XYPoint class as arguments in this method and then initialize the XYPoint object as well as allocate memory for it inside the method? Otherwise I have to create an XYPoint object in my main program and pass it as an argument which is a lot more code to type.
-(id) initWithWidth:(int)w andHeight:(int)h andOrigin:(XYPoint *)o
{
self = [super init];
if (self) {
[self setWidth: w andHeight: h];
self.origin = o;
}
return self;
}
P.S.- I am new to programming and Objective C so pardon me if I have stated something technically wrong in my question. Thanks!
Personally--I try to avoid initializers that take parameters. I think it leads to writing lots more code and inflexibility. I use designated initializers for just 2 things:
initializing an object with properties that must not be changed after the object is initialized
initializing an object with properties that are absolutely needed to construct it and cannot be specified later
In general, for Rectangle class, I'd make the use like this:
Rectangle * r = [ [ Rectangle alloc ] init ] ;
r.x = x ;
r.y = y ;
r.origin = o ;
// use r
and not use the designated initializer pattern at all except for the conditions outlined above. (For example, creating immutable Rectangle instances)
Finally, there's probably no need to create a Rectangle class--just use CGRect/NSRect primitive structures.

Constructor method not being called properly

I made a class called Player.h. The constructor is such:
-(Player*) construct
{
health = 100;
width = 50;
height = 50;
return self;
}
And it is in my header file as -(Player*) construct;
To verify I call my getWidth method and the other getters and all return as 0
Is this the right way to make a constructor? I'm trying to incorporate better OOP practices and this is the first time in Obj-C I'm really using objects
Usually, you create objects in Objective-C by way of calling alloc/init, in your case [[Player alloc] init]. Just overwrite the init method in your class - it already has the right skeleton. Do not remove the self = [[super alloc] init] line.
If you want your object to be constructed, you need to allocate in initialize it. While you can call your method -construct, it's traditionally called -init or -initWith<Blah> where <Blah> is some information like a rectangle or other useful value. You'd create an object like this:
Player* newPlayer = [[Player alloc] init];