isEqual is not working properly in xcode - ios

My code looks like proper but isEqual used to compare two object is not working. i'm new to iOS. there is no much resource to check. [box2 isEqual:box1] is always gives me No. but it supposed to give Yes. anything wrong in my code, please suggest me the correct thing. thanks in advance. expecting best suggestion .
#import <Foundation/Foundation.h>
#interface Box:NSObject
{
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
}
#property(nonatomic, readwrite) double height; // Property
-(double) volume;
//-(id)initWithLength:(double)l andBreadth:(double)b;
#end
#implementation Box
#synthesize height;
-(id)init
{
self = [super init];
if(self)
{
length = 2.0;
breadth = 3.0;
}
return self;
}
-(double) volume
{
return length*breadth*height;
}
#end
#class Box;
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Box *box1 = [[Box alloc]init]; // Create box1 object of type Box
Box *box2 = [[Box alloc]init];
NSLog (#"hello world");
box1.height = 5.0;
NSLog (#"%ld",[box1 volume]);
if([box2 isEqual:box1])
{
NSLog(#"%b",[box2 isEqual:box1]);
}
[pool drain];
return 0;
}

You have to override isEqual method in your object.
e.g. in your "#implementation Box"
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[Box class]]) {
Box*box = (Box*)object;
return self.height == box.height; // compare heights values
}
return [super isEqual:object];
}

isEqual: works just fine and as documented, but not as you expected.
isEqual: is a property of NSObject. It returns YES if two object pointers are the same, and NO if they are not. If you want a different result, you need to override isEqual for your Box class. Note that the argument to isEqual: could be anything, not just a Box, so you need to be careful.
Obviously you shouldn't declare instance variables put properties, so the code below assumes that. The way your code is written, you will have some confusion with an instance variable height, a property height, and an instance variable _height, which will give you headaches. It also assumes that you are not subclassing Box (for example you could have a coloured box, which should not be equal to a Box with same width, height and breadth).
- (BOOL)isEqual:(id)other
{
if (! [other isKindOfClass:[Box class]]) return NO;
Box* otherBox = other;
return _length == other.length && _breadth == other.breadth
&& _height == other.height;
}

Related

Format specifies type ‘int’ but the argument has type ‘float’

If I define a constant as an integer, how is it recognised as a float ?
I built and ran the code below without problems under Xcode 6.1.1. The problem only appeared after I upgraded to Xcode 7.2 (7C68) and El Capitan.
iOSCircleView.m
int const SECTORS_80 = 80;
#implementation iOSCircleView
- (void)ViewSettings_OverView {
sectors = SECTORS_80;
NSLog(#"sectors %i", sectors);
}
When I run it NSLog reports a warning
Format specifies type ‘int’ but the argument has type ‘float’
The console displays
2016-01-07 10:05:00.210 AutoDrawUI[1298:555384] sectors 855670784
If I change NSLog command to
NSLog(#"sectors %f”, sectors);
and rerun the code, there is no warning but the console displays
2016-01-07 10:38:01.547 AutoDrawUI[1330:632806] sectors 80.000000
This seems like a bug. But maybe I’ve missed something
Here is the Original Code
int const SECTORS_80 = 80;
#implementation iOSCircleView
// Initialisation
// frame a view for drawing
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
totalCircles = [[NSMutableArray alloc] init];
centre = [[NSMutableArray alloc] init];
ring = [[NSMutableArray alloc] init];
// Set background color
self.backgroundColor = [UIColor grayColor];
}
return self;
}
// initialise single target view (7 others are commented out)
- (void)drawRect:(CGRect)rect {
[self ViewSettings_OverView]; // 1. "launch view" ??
}
Running (note: the problem occurs before it gets here)
// run
- (void)autoDrawUI {
[self drawCircle];
}
Here is the full class (unedited)
// initialise display settings
- (void)ViewSettings_OverView {
viewState = 0;
// number of dots on perimeter
sectors = SECTORS_80; // WARNING HERE
limit = sectors;;
if (sectors <= 0) { // WARNING HERE
show = FALSE; // disable
} else
show = TRUE; // enable perimeter dots
// radius of large circle perimeter
uberRadius = UBERRADIUS_52;
// radius of dots on large circle perimeter
dotRadius = DOTRADIUS_4;
dotsFilled = TRUE; // outline
oneColour = TRUE; // every colour
alternateDots = TRUE; // alternately filled and coloured
oddEvenState = 1; // PlayView
ringDot = RINGDOT_HIDE; //_SHOW104
centreDotFilled = FALSE; // fill or outline
centreDot = CENTREDOT_HIDE; //CENTREDOT_SHOW26;
centreDotFilled = TRUE; // fill or outline
NSLog(#"View %i", viewState);
NSLog(#"sectors %i", sectors); // WARNING HERE
NSLog(#"show %i", show);
[self centreReference];
}
And here is the .h file
#import <UIKit/UIKit.h>
#interface iOSCircleView : UIView
{
NSMutableArray *totalCircles;
NSMutableArray *sectorDots;
// following Stackoverflow 08/02/2015 ?
NSMutableArray *centre; // 1 of 5 (Global View), centre (Wait View), even bell icon (Play View)
NSMutableArray *ring; // odd bell icon (Play View)
// TO DO
NSMutableArray *sectorDotsCyan; // 16 moons surround 1 of 5 buttons on Global View
NSMutableArray *sectorDotsRed; // 16 moons 2
NSMutableArray *sectorDotsYellow; // 16 moons 3
NSMutableArray *sectorDotsMagenta; // 16 moons 4
NSMutableArray *sectorDotsGreen; // 16 moons 5
int dotCount, // working
colourIndex, // working
toggleIndex, // working
imageIndex,
limit, // working
selectedZone, // working
selectedPhone,
selectedState,
state,
viewState, // working
ringDotFilled,
centreDotFilled, // working
dotsFilled, // working
labelShown,
oneColour, // working
alternateDots,
i, // working
show; // working
BOOL shakeTap, // PlayView
oddEvenPhone, // LocalView
oddEvenState; // working
float uberX, // working
uberY, // working
uberRadius, // working
uberAngle, // working
labelX,
labelY,
dotRadius, // working
sectors, // working
x, // working
y, // working
ringDot, // working
centreDot, // working
textOffset, // working
startAngle,
dotAngle,
endAngle,
angle;
CGPoint dotPosition;
CGRect boxBoundary;
CGContextRef context;
}
#property(nonatomic,retain) UIColor* fillColor;
#property(nonatomic,retain) UIColor* circle;
#property(nonatomic,retain) UIImage* anImage;
- (void)drawCircle;
- (void)drawRosette;
#end
... and the problem becomes immediately obvious. I declared sectors as float and somehow how I got away with this previously. My brain's still on holiday!
Your issue has nothing to do with SECTORS_80. It's all about the sectors variable since that is the variable you are logging. You didn't specify how it is declared but clearly the error indicates that you declared it as a float.
Change:
float sectors;
to:
int sectors;
Or change the format to use %f. Either way will remove the error. But only one is really appropriate depending on your needs.

Declare typedef struct Variable

I have this typedef in my .h file
typedef struct
{
NSInteger openTime;
NSInteger closeTime;
} ShopHours;
Now I want to declare it in my .m file and then like you would a NSString that you want to use throughout the .m.
SO basically I want to initialize it, as an array of the typedef and then so I can assign it a vaule in my viewDidLoad method, as I see fit.
So basically I want to declare it like
ShopHours weekSchedule[] = {};
And then in viewDidLoad
weekSchedule[] = {//my data}
But when I try to declare and use it like this I get compile error
Field has incomplete type 'SHopHours[]'
Thanks for the help in advance
The compiler needs to know how big the weekSchedule variable is in order to put some memory aside for it. You could declare it as shopHours weekSchedule[7] for example.
I tried, the problem was you need to decleare the size of the array first :
#interface LoginScreen (){
ShopHours aa[5];
}
- (void)viewDidLoad
{
[super viewDidLoad];
for (int i = 0; i < 5; i++) {
aa[i].closeTime = 5+i;
aa[i].openTime = 10 + i;
}
}
In another method I logged it:
for (int i = 0; i < 5; i++) {
NSLog(#"Open Time %ld\n",(long)aa[i].openTime);
NSLog(#"Close Time %ld\n",(long)aa[i].closeTime);
}
Works just fine.
If you want dynamic memory allocation,you can do it
#interface LoginScreen (){
ShopHours *aa;
}
- (void)viewDidLoad
{
[super viewDidLoad];
aa = malloc(sizeof(ShopHours) * 5);
for (int i = 0; i < 5; i++) {
aa[i].closeTime = 5+i;
aa[i].openTime = 10 + i;
}
}
Hope this helops.. :)

Object orientation - iOS isEqual method in superclass, will it work correctly for subclasses?

I have two isEqual method that looks like this:
- (BOOL)isEqualToObject:(IdentifiableObject *)object {
if ([self identifier] == [object identifier])
return YES;
return NO;
}
- (BOOL)isEqual:(id)otherObject {
if (otherObject == self)
return YES;
else if (!otherObject || ![otherObject isKindOfClass:[self class]])
return NO;
return [self isEqualToObject:otherObject];
}
This is placed in a generic superclass that other classes inherit from.
Will this work as expected (or as I want)? Or should the isEqual methods be placed inside each subclass?
Since your subclasses will inherit this method, it works as expected. In your isEqual: method you make use of self, so you're referring to current class instance.
Here an example:
#interface ClassA : NSObject
#property (nonatomic,strong) NSString* myName;
#end
// -------
#implementation ClassA
- (BOOL)isEqualToObject:(ClassA *)object {
if (self.myName == object.myName)
return YES;
return YES;
}
- (BOOL)isEqual:(id)otherObject {
if (otherObject == self)
return YES; // instead of "![otherObject isKindOfClass:[self class]]"
else if (!otherObject || !([otherObject class] == [self class]))
return NO;
return [self isEqualToObject:otherObject];
}
#end
sample usage
ClassA* a1 = [[ClassA alloc]init];
ClassA* a2 = [[ClassA alloc]init];
a1.myName = #"joe";
a2.myName = #"joe";
ClassB* b1 = [[ClassB alloc]init];
ClassB* b2 = [[ClassB alloc]init];
b1.myName = #"frank";
b2.myName = #"frank";
ClassC* c1 = [[ClassC alloc]init];
ClassC* c2 = [[ClassC alloc]init];
c1.myName = #"donnie";
c2.myName = #"donnie";
NSLog(#" a1 == a2 -> %hhd",[a1 isEqual:a2]); // Match
NSLog(#" b1 == b2 -> %hhd",[b1 isEqual:b2]); // Match
NSLog(#" c1 == c2 -> %hhd",[c1 isEqual:c2]); // Match
NSLog(#" a1 == b1 -> %hhd",[a1 isEqual:b1]); // Doesn't match
NSLog(#" b1 == c1 -> %hhd",[b1 isEqual:c1]); // Doesn't match
NSLog(#" c1 == a1 -> %hhd",[c1 isEqual:a1]); // Doesn't match
It will work within bounds...
If you implement isEqual: you should also implement hash so that the equality requirements are maintained. If you don't, and you place your custom object into a collection you may get some unexpected results.
Your custom implementation also doesn't consider subclass custom properties unless they override the superclass implementation. Whether this is an issue depends on how you define equality.
In a lot of cases it's better to not override isEqual: but instead to add your own custom comparison method - but, it depends how you want the equality to be used.
Basically, be careful what you override and ensure that you satisfy all of the corresponding equality requirements.

Clearing/Resetting all string and variables

I have a routine in xcode that calls a function and spits out a result to a UITextView element on an iPhone app.
Before the routine is started I want to reset all NSString objects/values and any variables that I used to calculate the result. Is there a quick way to do this?
So far no matter what I retype in the UITextField it just gets ignored after the initial calculation.
Code for the solve button :-
- (IBAction)Solve:(id)sender {
//
// Reset strings and variable code here
//
[_Number1 resignFirstResponder];
[_Number2 resignFirstResponder];
[_Number3 resignFirstResponder];
[_Number4 resignFirstResponder];
[_Number5 resignFirstResponder];
[_Number6 resignFirstResponder];
[_TargetNum resignFirstResponder];
// Dismiss Keyboard
int numb1,numb2,numb3,numb4,numb5,numb6,numTar;
numb1 = [self.Number1.text intValue];
numb2 = [self.Number2.text intValue];
numb3 = [self.Number3.text intValue];
numb4 = [self.Number4.text intValue];
numb5 = [self.Number5.text intValue];
numb6 = [self.Number6.text intValue];
numTar = [self.TargetNum.text intValue];
mainLoopOne(numb1,numb2,numb3,numb4,numb5,numb6,numTar);
// Start calculation with field values
readAnswers = #"Please read answers bottom-up.\n";
cantCalc = NULL;
finalResult = #"";
if (numb1 != 0) {
int ii;
for(ii = 0; ii < 6 ; ii++) {
if (allAnswers[ii] != NULL) {
finalResult = [finalResult stringByAppendingFormat:#"%#", allAnswers[ii]];
}
}
if (finalResult == NULL) {
cantCalc = #"Unfortunately, that doesn't seem to be possible, as there was no answer calculated.\n";
readAnswers = #"";
}
CompileText = [NSString stringWithFormat:#"%#\n%#\nfrom %d combination tries.", readAnswers, finalResult, countCombi];
[self.TextWin setText:CompileText];
}
countCombi = 0;
}
#end
Most of the strings and variables are set just under the #import "ViewController.h" so they are global :-
NSString *readAnswers;
NSString *cantCalc;
NSString *finalResult;
NSString *allAnswers[10];
NSString *CompileText;
NSString *readAnswers;
NSString *finalResult;
#define DIV 0
#define MUL 1
#define ADD 2
#define SUB 3
int n1,n2,n3,n4,n5,n6;
int answer_counter = 0;
int tar2 = 0;
int number[6];
int target = 0;
int used[6];
int countCombi;
Thanks.
Create a reset method that alloc inits every string in your view controller again. You can set the textview's text to "".
In theory you could write a helper class that you pass in an object (your view controller) and it uses introspection to reset everything that is a string or whatever you want. This is a good approach for modularity and reusability, however it requires knowledge of c, and introspection.
Yes whatever williams say thats correct. Implement like below:
-(IBAction)doClear:sender
{
[Yourtextview setString];
}

My modal keeps eating memory

I am experiencing a rather serious issue with my iPhone app using ARC.
I have a viewcontroller (lets call this A). This viewcontroller opens a navigationcontroller as a modal which runs through 3 different viewcontrollers (lets call these 1, 2 and 3). After viewing number 3 the navigationcontroller closes and we're back to A again.
So the flow is: A opens navigationcontroller and goes through 1->2->3 and then it closes again.
Every time I go through this flow i lose memory. I've searched through all my files looking for any retain og strong properties, un-invalidated timers or similar in order to solve this problem.
I have one idea, which might be the problem. At viewcontroller 1 i present a animation using coreanimation and a sprite. I'm using a implementation made by someone else. It seems like if i disable the animations the memory used seems quite constant (and thereby no memory loss). I have modified the implementation a bit to use ARC. This is the implementation I use for my sprite animations:
MCSpriteLayer.h
//
// MCSpriteLayer.h
//
// Created by Miguel Angel Friginal on 8/20/10.
// Copyright 2010 Mystery Coconut Games. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#interface MCSpriteLayer : CALayer {
unsigned int sampleIndex;
}
// SampleIndex needs to be > 0
#property (nonatomic) unsigned int sampleIndex;
// For use with sample rects set by the delegate
+ (id)layerWithImage:(CGImageRef)img;
- (id)initWithImage:(CGImageRef)img;
// If all samples are the same size
+ (id)layerWithImage:(CGImageRef)img sampleSize:(CGSize)size :(int)useRetina;
- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;
// Use this method instead of sprite.sampleIndex to obtain the index currently displayed on screen
- (unsigned int)currentSampleIndex;
#end
MCSpriteLayer.m
//
// MCSpriteLayer.m
//
// Created by Miguel Angel Friginal on 8/20/10.
// Copyright 2010 Mystery Coconut Games. All rights reserved.
//
#import "MCSpriteLayer.h"
#implementation MCSpriteLayer
#synthesize sampleIndex;
#pragma mark -
#pragma mark Initialization, variable sample size
- (id)initWithImage:(CGImageRef)img;
{
self = [super init];
if (self != nil)
{
self.contents = (__bridge id)img;
sampleIndex = 1;
}
return self;
}
+ (id)layerWithImage:(CGImageRef)img;
{
MCSpriteLayer *layer = [(MCSpriteLayer*)[self alloc] initWithImage:img];
return layer;
}
#pragma mark -
#pragma mark Initialization, fixed sample size
- (id)initWithImage:(CGImageRef)img sampleSize:(CGSize)size;
{
self = [self initWithImage:img];
if (self != nil)
{
CGSize sampleSizeNormalized = CGSizeMake(size.width/CGImageGetWidth(img), size.height/CGImageGetHeight(img));
self.bounds = CGRectMake( 0, 0, size.width, size.height );
self.contentsRect = CGRectMake( 0, 0, sampleSizeNormalized.width, sampleSizeNormalized.height );
}
return self;
}
+ (id)layerWithImage:(CGImageRef)img sampleSize:(CGSize)size :(int)useRetina;
{
CGSize newSampleSize;
if(useRetina == 1) {
// Supporting retina displays
if ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)] &&
([UIScreen mainScreen].scale == 2.0)) {
newSampleSize = CGSizeMake(size.width*2, size.height*2);
} else {
newSampleSize = size;
}
} else
newSampleSize = size;
MCSpriteLayer *layer = [[self alloc] initWithImage:img sampleSize:newSampleSize];
return layer;
}
#pragma mark -
#pragma mark Frame by frame animation
+ (BOOL)needsDisplayForKey:(NSString *)key;
{
return [key isEqualToString:#"sampleIndex"];
}
// contentsRect or bounds changes are not animated
+ (id < CAAction >)defaultActionForKey:(NSString *)aKey;
{
if ([aKey isEqualToString:#"contentsRect"] || [aKey isEqualToString:#"bounds"])
return (id < CAAction >)[NSNull null];
return [super defaultActionForKey:aKey];
}
- (unsigned int)currentSampleIndex;
{
return ((MCSpriteLayer*)[self presentationLayer]).sampleIndex;
}
// Implement displayLayer: on the delegate to override how sample rectangles are calculated; remember to use currentSampleIndex, ignore sampleIndex == 0, and set the layer's bounds
- (void)display;
{
if ([self.delegate respondsToSelector:#selector(displayLayer:)])
{
[self.delegate displayLayer:self];
return;
}
unsigned int currentSampleIndex = [self currentSampleIndex];
if (!currentSampleIndex)
return;
CGSize sampleSize = self.contentsRect.size;
self.contentsRect = CGRectMake(
((currentSampleIndex - 1) % (int)(1/sampleSize.width)) * sampleSize.width,
((currentSampleIndex - 1) / (int)(1/sampleSize.width)) * sampleSize.height,
sampleSize.width, sampleSize.height
);
}
#end
Is this implementation somehow not realeasing correctly or retaining anything? Thanks in advance.
Update
- I am using Instruments to measure the memory. I am using the Memory Monitor where I keep an eye on the Physical Memory Free
- The image is created like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"round_start.png" ofType:nil];
CGSize fixedSize = CGSizeMake(320, 480);
mascot = [MCSpriteLayer layerWithImage:[UIImage imageWithContentsOfFile:path].CGImage sampleSize:fixedSize :0];
mascot.frame = CGRectMake(ANIMATION_X, ANIMATION_Y, ANIMATION_WIDTH, ANIMATION_HEIGHT);
[self.view.layer addSublayer:mascot2];
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"sampleIndex"];
anim.delegate = self;
anim.fromValue = [NSNumber numberWithInt:1];
anim.toValue = [NSNumber numberWithInt:52];
anim.duration = ANIMATION_DURATION;
anim.repeatCount = 1;
[mascot addAnimation:anim forKey:nil];
- I've been experiencing with closing the modal with
[self dismissModalViewControllerAnimated:YES];
and
[self.navigationController dismissModalViewControllerAnimated:YES];
Dismissing the navigation controller does not release it, assuming you have a strong reference to it (that would need to get nil'd). In the view controllers used by it, add a log message in the dealloc method, so you know they are getting dealloced (you can do this for any subclassed item). If needed you can create a simple UINavigation subclass for the sole purpose of adding a message in dealloc). You will find that one or more of these items are not getting dealloced, and then you need to figure out if they are retained by a property/ivar or because they still have a superview.

Resources