insertObject:AtIndex:0 does not work properly - ios

I have this function in which the function insertObject:AtIndex:0 behaves weird . After inserting all objects to the NSMutableArray cardViewControllers, the final element is always nil.I did alloc init the cardViewControllers at the beginning in the init method.
- (void)reloadCardViews;
{
// Add the restaurants onto view
[self removeAllCards];
for (int i = 0; i < NO_OF_CARDS; i++) {
SHCCardVC *vc = [[SHCCardVC alloc] initWithAppearanceIndex:i];
vc.delegate = self;
[cardViewControllers insertObject:vc atIndex:0];//it's behaving weird here
[self addChildViewController:vc];
// set card position to center of the container
vc.view.center = CGPointMake(_cardContainer.frame.size.width / 2, _cardContainer.frame.size.height / 2);
[_cardContainer addSubview:vc.view];
}
_currentCardViewIndex = 0;
_currentCardIndex = 0;
}

What does [self removeAllCards] do? I suspect that you call [cardViewControllers removeAllObjects]? Have you tried using [cardViewControllers addObject:vc]? If this works and the order is important, walk trough your for loop from behind with i--.
Also make sure your objects are not nil and your array is mutable and also initialized. I had a similar problem with an uninitialized mutable array.

You can't add nil objects to arrays, it's a runtime error. So something must be going weird with the retain count, that's all I can think at the moment. NSArray retains objects that are within it, so the object is being released one too many times somewhere.
Is this a possibility?

Related

How to call method of another class which uses self in objective C

RKSwipeBetweenViewControllers class below (Github library to swipe between controllers) it also provides an option where i can tap on buttons(which are names of viewControllers) how ever i am implementing a next button in each of my viewcontroller, so that if a user doesnot know he can swipe he can simply click on next button to go to next controller:
-(void)setupSegmentButtons {
navigationView = [[UIView alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.navigationBar.frame.size.height)];
NSInteger numControllers = [viewControllerArray count];
if (!buttonText) {
buttonText = [[NSArray alloc]initWithObjects: #"Login",#"Personal",#"Contact",#"Club Info",nil]; //%%%buttontitle
}
for (int i = 0; i<numControllers; i++) {
RKButton = [[UIButton alloc]initWithFrame:CGRectMake(X_BUFFER+i*(self.view.frame.size.width-2*X_BUFFER)/numControllers-X_OFFSET, Y_BUFFER, (self.view.frame.size.width-2*X_BUFFER)/numControllers, HEIGHT)];
[navigationView addSubview:RKButton];
RKButton.tag = i; //%%% IMPORTANT: if you make your own custom buttons, you have to tag them appropriately
[RKButton setBackgroundColor:ThemeColor];//%%% buttoncolors
[RKButton addTarget:self action:#selector(tapSegmentButtonAction:) forControlEvents:UIControlEventTouchUpInside];
//[RKButton addTarget:self action:#selector(CalltapSegmentButtonAction) forControlEvents:UIControlEventTouchUpInside];
[RKButton setTitle:[buttonText objectAtIndex:i] forState:UIControlStateNormal]; //%%%buttontitle
[_RKSwipeRegisterModal.RKButtonArray addObject:RKButton];
NSLog(#"_RKSwipeRegisterModal.RKButtonArray %#",_RKSwipeRegisterModal.RKButtonArray);
}
pageController.navigationController.navigationBar.topItem.titleView = navigationView;
[self setupSelector];
}
and the selector method is below:
-(void)tapSegmentButtonAction:(UIButton *)RKButton {
if (!self.isPageScrollingFlag) {
NSInteger tempIndex = self.currentPageIndex;
__weak typeof(self) weakSelf = self;
//%%% check to see if you're going left -> right or right -> left
if (RKButton.tag > tempIndex) {
//%%% scroll through all the objects between the two points
for (int i = (int)tempIndex+1; i<=RKButton.tag; i++) {
[pageController setViewControllers:#[[viewControllerArray objectAtIndex:i]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL complete){
//%%% if the action finishes scrolling (i.e. the user doesn't stop it in the middle),
//then it updates the page that it's currently on
if (complete) {
[weakSelf updateCurrentPageIndex:i];
}
}];
}
}
//%%% this is the same thing but for going right -> left
else if (RKButton.tag < tempIndex) {
__weak typeof(self) kjhg = self;
for (int i = (int)tempIndex-1; i >= RKButton.tag; i--) {
[pageController setViewControllers:#[[viewControllerArray objectAtIndex:i]] direction:UIPageViewControllerNavigationDirectionReverse animated:YES completion:^(BOOL complete){
if (complete) {
[weakSelf updateCurrentPageIndex:i];
}
}];
}
}
}
}
now i have created a global array of button created in above method and passed it in below code from another class.
Now i want to call the above method from first class using below code:
-(void)callMethodOfSecondClass {
UIButton *button = [globalArrayOfButtons objectAtIndex:1];
[rkSwipeControllrObject tapSegmentButtonAction:button];
}
Now the issue is obviously self.currentPageIndex is nil in second class.
How do i give it the value of its original earlier self.
Any help please
Now the issue is obviously self.currentPageIndex is nil in second
class.
How do i give it the value of its original earlier self.
You cannot. self always refers to the object that owns the method that's executing. If self.currentPageIndex is nil, that's because the object that you're sending the message methodOfSecondClass: to has nil for it's currentPageIndex property.
It's hard to see what'g going on in your code because your -callMethodOfSecondClass: method doesn't actually ever call -methodOfSecondClass: despite your claim to the contrary. Could you make sure you've provided the real code that exhibits the problem?
One possible problem is that you've got a case of mistaken identity. You may have two (or more!) instances of SecondClass, where you know the currentPageIndex of one is valid, but you're sending the -methodOfSecondClass : message to a different instance than the one you expect.
'currentPageIndex' is not nil inside second class, it is only nil when
it is called from another class. i understand when i 'rkSwipeControllr
= [SecondClass new];' it takes new memory, for which 'currentPageIndex' should be 'nil'. But is there any way to assign it
the same memory address
When you say rkSwipeControllr = [SecondClass new], rkSwipeControllr gets the address of a new instance of SecondClass, completely separate from any you've created previously.
I think you'll benefit from considering the difference between a class and an instance of that class. The class SecondClass defines a kind of object, but the class itself doesn't store the information you're interested in, like currentPageIndex. To use the class, you have to create an instance of that class, i.e. an object whose type is SecondClass.
It's like the difference between, say, Ford F-150, which is a kind of pickup truck, and ios_Dev's Ford F-150, which is an actual truck (or would be if you drove an F-150). Let's say I go to the dealer and buy a F-150, and I park it in my driveway and put my chainsaw in the back. Later that day, when I need my chainsaw, I have to go to that particular truck to get it. If I instead go to the dealer and buy another F-150, I can't reasonably expect to find my chainsaw in the back, right? And so it is with instances of SecondClass -- if you create a new instance of SecondClass and set it's currentPageIndex, you need to use that same object if you want to get that currentPageIndex value back. To do that, you need to keep a strong reference to the object when you create it. If you don't have at least one strong reference to the object, then nobody will remember where the object is, and the Objective-C runtime will helpfully destroy the object. One way to keep that strong reference is in a property marked strong in the object that needs to refer to the object later.

Reset property within lazy getter / after each access

I feel like there is a more regulation way to do what I am doing in, either by some iOS specific thing, or pattern I'm aware of. I'm trying to create an NSMutableArray variable, that essentially acts as temporary storage for a logger class. Each time the array is accessed, I want to either lazily instantiate it, or set it to nil. The way I am thinking of doing it seems a little hacky and I'm looking for some input?
- (NSMutableArray)myArray {
if (!_myArray) {
_myArray = [[NSMutableArray alloc] init];
} else {
_myArray = nil;
}
return _myArray;
}
The effect I'm hoping to achieve is using a logger that is logging details about network requests - http codes, URLs, repsonse times, etc. I want the logger to amalgamate all this output in this storage array. Later on, when I'm hitting an API, I want to take the contents of this array, and send it up to the API, and I also want the array to reset (so the array is essentially a log of network request data since the last time the app hits the API, versus a log of what has happened since the app launched.
I realise that I could do this manually by niling the array when I access it, but I'm trying to do this in a more of a plug and play way, where it you don't need to worry if someone forgets to nil the array etc
The effect that you are trying to achieve is perfectly legitimate, but you shouldn't try to achieve it with a getter alone: the very fact that a simple getter could reset something back to nil would be counter-intuitive to your readers.
Instead, you should make two methods - one to prepare the array, and another one to harvest it, and replace with a fresh nil:
- (NSMutableArray*)myArray {
if (!_myArray) {
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
- (NSMutableArray*)resetArray{
NSMutableArray *res = _myArray;
_myArray = nil;
return res;
}
Now the sequence of operations becomes intuitively clear: you get myArray as many times as you wish, add as many items as you need, and then when you are done call resetArray. This would get you a complete array with all the data, and reset the object to be ready for the next call:
for (int col = 0 ; col != 10 ; col++) {
[log.myArray addObject:[self getDataForIndex:col]];
}
NSMutableArray* logArray = [log resetArray];
What you're doing doesn't make any sense to me.
Creating it empty if it doesn't exist makes sense.
Setting it to nil if it does exist does not make sense.
The usual pattern for lazy loading is to use a property with a getter:
#property (nonatomic, retain) NSMutableArray * myArray;
and then the implementation:
//Custom getter
-(NSMutableArray*) myArray;
{
if (!_myArray)
_myArray = [[NSMutableArray alloc] init];
return _myArray;
}
Then you ALWAYS refer to the array using the getter. If it hasn't yet been created, the getter creates and returns the empty array. If it does exist, it returns the existing one.

My Sprite AI is working incorrectly. Why?

I want my two enemies to be set on attack mode, however as it stands only the last enemy added is being set on attack mode.
Is there any way around this? Any tips or suggestions is appreciated. If you need more code please let me know.
-(void)ViewDidLoad {
for (_enemyPoint in [self.enemyGroup objects]) {
self.enemy = [[CCSprite alloc] initWithFile:#"Icon.png"];
self.enemy.scale = 32.0f/57.0f;
self.enemy.position = CGPointMake([_enemyPoint[#"x"] integerValue], [_enemyPoint[#"y"] integerValue]);
[self addChild:self.enemy];
}
self.pathfinder = [HUMAStarPathfinder pathfinderWithTileMapSize:self.tileMap.mapSize
tileSize:self.tileMap.tileSize
delegate:self];
[self enemyAttack];
}
- (void)enemyAttack{
self.epath = [self.pathfinder findPathFromStart:self.enemy.position
toTarget:self.player.position];
self.eactions = [NSMutableArray array];
for (_epointValueInPath in self.epath) {
self.epoint = _epointValueInPath.CGPointValue;
self.emoveTo = [CCMoveTo actionWithDuration:1.0f position:self.epoint];
[self.eactions addObject:self.emoveTo];
}
self.esequence = [CCSequence actionWithArray:self.eactions];
[self.enemy runAction:self.esequence];
}
Look at your loop in viewDidLoad. First, you use an iVar as the loop variable. Probably not what you want. Second, you assign self.enemy in each iteration, but you call enemyAttack after the loop has completed.
Further, enemyAttack does not take any parameters, so it uses internal state. Since it is called after the loop has iterated over all objects, self.enemy will always be the last object in the collection (if there is anything in the collection).
Thus, it is not surprising that you only see the last item being activated as an enemy.
Have you tried to put the [self enemyAttack]; invocation inside the for loop?

NSMutableArray Allocate then replaceObjectAtIndex

I have a NSMutableArray that i define in the header file as:
#property (strong, nonatomic) NSMutableArray *tempPhotosArray;
Then i allocate as:
_tempPhotosArray = [[NSMutableArray alloc] init];
What i'd like to know is if i then go to replaceObjectAtIndex the program will complain on an out of bounds. I want to keep only a set number of items in that array, so is it possible to do a insert or replace? i.e. if at index 0 it is empty do an insert, if there is an object already replace it?
Thanks
i think i agree with Hani Ibrahim. Since you said you only want to keep a set number of objects in the array. So how many you want?
// add these code when you initialize the array
int aSetNumber = 5;
_tempPhotosArray = [[NSMutableArray alloc] init];
for (int i = 0; i < aSetNumber; i++)
{
[_tempPhotosArray addobject: [NSNull null]];
}
i guess then you can do whatever you want, i don't know what exactly you want to do in this case, but i would check if the object in that position is NSNUll, if so, replace that, if not, i don't know what you want them
//use these code when you trying to insert the real object
if([[_tempPhotoArray objectAtIndex:anIndex] isKindOfClass: [NSNull class]])
{
//replace it here
}
As to why you are getting an error, what everyone else wrote is accurate, but....
The description of what you want doesn't match what an NSArray is. It sounds like you want a list of up to 5 items and never more than 5. It might be that if you try to add a 6th item the "oldest" goes away. Like a "recently opened" file history. You can make this type of functionality with an NSArray, but that's not what it is out of the box.
I would suggest making your own object class. I'm not going to write all the code for you, because this sounds suspiciously like programming homework, but I will point you in the correct direction.
FivePack <-- our class
NSArray *storage; <-- where we house the data
// a public method which lets you add things.
- (void)addItem:(id)item {
int indexOfLastItemInArrayToSave = 4;
if (storage.length < 4)
indexOfLastItemInArrayToSave = length-1;
NSRange range = NSMakeRange(0, indexOfLastItemInArrayToSave);
NSArray *temp = [storage subArrayWithRange:range];
// now create a new array with the first item being "item" that
// was passed in and the rest of the array being the contents of temp.
// Then save that to storage.
}
What you want to do with the data and writing something to get it from your new object is up to you, because I'm not sure how you want to do it.
There are no objects in the array when you initially created it, so there is nothing to replace.
Like this?
if([_tempPhotosArray count] > 0)
//replace object
else
//add object to array

Automatic Reference Counting and finalize

Quick question: I use lots of NSObject derived classes and am wondering how to properly cleanup class properties that may own instances of other classes (in the snippet below this is an array of custom class instances). Is my usage of new and finalize below correct?
My understanding is that new is a convenience method that calls both alloc and init, and finalize gets called before dealloc - at least this is what I have gleaned from reading the docs. Do I have this right?
Thanks for any tips/best practices, etc!
- (id)new {
waffleArray = [[NSMutableArray alloc] initWithCapacity:kCellCount];
for (int i = 0; i < kCellCount; i++) {
WaffleCell * cell = [WaffleCell new];
[waffleArray addObject:cell];
}
return self;
}
// clean up
- (void)finalize {
[waffleArray removeAllObjects];
waffleArray = nil;
[super finalize];
}
new on NSObject is a class method, not an instance method as you have it. Also I don't really see why you'd overload new. It would be more common to overload init so something like this:
- (id)init {
if ((self = [super init])) {
waffleArray = [[NSMutableArray alloc] initWithCapacity:kCellCount];
for (int i = 0; i < kCellCount; i++) {
WaffleCell * cell = [WaffleCell new];
[waffleArray addObject:cell];
}
}
return self;
}
As for finalize, you really don't need to do that. This is what Apple says about it:
The garbage collector invokes this method on the receiver before disposing of the memory it uses. When garbage collection is enabled, this method is invoked instead of dealloc.
With ARC enabled you wouldn't need to do anything and since the garbage collector will not be running anyway, finalize won't get called anyway. ARC will automatically generate code which will release waffleArray in dealloc for you, which is enough for proper memory management in this case because waffleArray's retain count will then drop to 0, be deallocated itself which will go and release the objects in the array.

Resources