iOS custom object initialization Error - ios

I have a custom object ProductCategory.
.h file:
#import <Foundation/Foundation.h>
#interface ProductCategory : NSObject
#property int productCategoryId;
#property NSString *name;
#property NSArray *children;
#property int parentCategoryId;
- (id)initWithId:(int)productCategoryId name:(NSString*)name;
- (id)initWithId:(int)productCategoryId name:(NSString*)name children:(NSArray*)chidren parentCategoryId:(int)parentCategoryId;
#end
.m file:
#import "ProductCategory.h"
#implementation ProductCategory
- (id)init {
if((self = [super init])) {
self.parentCategoryId = 0;
}
return self;
}
- (id)initWithId:(int)productCategoryId name:(NSString*)name {
if((self = [super init])) {
self.productCategoryId = productCategoryId;
self.name = name;
self.parentCategoryId = 0;
}
return self;
}
- (id)initWithId:(int)productCategoryId name:(NSString*)name children:(NSArray*)chidren parentCategoryId:(int)parentCategoryId {
if((self = [super init])) {
self.productCategoryId = productCategoryId;
self.name = name;
self.children = chidren;
self.parentCategoryId = parentCategoryId;
}
return self;
}
#end
It's just a normal object, I have done this 100000 times. The problem is, sometimes the instance of this object returns "0 objects" and sometimes returns the correct object.
For example, if I do this ProductCategory *category = [[ProductCategory alloc]init]; sometimes it returns a ProductCategory instance, and sometimes it returns "0 objects" so I cannot assign any value to this object.
I guess it should be something really stupid but I don't see it.

The way to fix it:
Restart XCode.
Why it happen?:
Apple should answer this question.
It seems a garbage in memory issue after a period of time using XCode.
Workarounds if you get trap there
#HotLicks is right about the advising of use NSLog and po to be sure about the state of that object.
Also you can invoke methods and read properties of the object in question by using expression command in debugger window after a breakpoint.

zevarito is on the right track. A bit more seems to solve the long-irritating problem:
Close the project.
Xcode -> Window -> Projects
For the project in question (and all others is probably a good housecleaning idea), click Derived Data -> Delete.
Close Xcode.
Close Simulator.
Restart Xcode and resume what you were doing.

Related

NSUndoManager removeAllActionsWithTarget crash

I have some abbreviated iOS Objective-C sample code (simplified from a larger project) that causes a crash in NSUndoManager that I can't explain.
Namely, when an object that is only held onto by the NSUndoManager deallocs (because it's beyond the levels of undo), and, according to the docs calls removeAllActionsWithTarget:self, I get an EXC_BAD_ACCESS.
// SimpleViewController.m
#interface ViewController ()
#property (nonatomic, strong) NSUndoManager *undoManager;
#end
#implementation ViewController
#synthesize undoManager;
// called from a simple button
- (IBAction)doItTapped:(id)sender
{
CoolObject *object = [CoolObject new];
object.undoManager = self.undoManager;
// according to docs, object will be retained by NSUndoManager here
// but target will not (which should be okay)
[self.undoManager registerUndoWithTarget:self selector:#selector(notCool:) object:object];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.undoManager = [NSUndoManager new];
self.undoManager.levelsOfUndo = 3;
}
and
// CoolObject.m
#implementation CoolObject
- (void)dealloc
{
[self.undoManager removeAllActionsWithTarget:self];
}
#end
After the 4th tap of the button (levelsOfUndo + 1), it crashes.
If I swap NSUndoManager with GCUndoManager, no crash.
Tested in iOS 10.2 sim and devices.
Thanks for any ideas!
Their are chances that you might be getting this error because self.undoManager is not retained at that point where you are using it. When the object is already deallocated and you try to access it, you will get bad access exception.
Try to change your code from this:
CoolObject *object = [CoolObject new];
to this:
#interface ViewController (){
CoolObject *object;
}
#property (nonatomic, strong) NSUndoManager *undoManager;
#end
#implementation ViewController
- (IBAction)doItTapped:(id)sender
{
object = [CoolObject new];
object.undoManager = self.undoManager;
// according to docs, object will be retained by NSUndoManager here
// but target will not (which should be okay)
[self.undoManager registerUndoWithTarget:self selector:#selector(notCool:) object:object];
}
#end
Hope this will help.
Just like me, you seem to have misinterpreted the admittedly inaccurately written documentation. The docs talk about "target", "object" and "target object" as if they were different things when they really mean exactly one and the same: the (id)target parameter of -removeAllActionsWithTarget:
In other words, in my opinion you should not need to call -removeAllActionsWithTarget: inside of CoolObject at all because CoolObject has been specified as the object of -registerUndoWithTarget:selector:object: whereas the target is your ViewController.
You may have to call -removeAllActionsWithTarget: in your NSViewController's -dealloc but even that is unnecessary in your example because your NSViewController owns the NSUndoManager and thus ViewController won't go away before undoManager does.

Assign value to NSUInteger in object after init

I have a subclass of NSObject. In this class I have an NSUInteger declared like this:
#interface Deck : NSObject
- (id)initDecks:(NSUInteger)decks;
- (Card *)drawCard;
#property (nonatomic, assign) NSUInteger drawnCards;
In my implementation file, I have the following code:
#import "Deck.h"
#implementation Deck
- (id)initDecks:(NSUInteger)decks {
self = [super init];
if (self) {
self.drawnCards = 1;
}
return self;
}
- (BJCard *)drawCard {
self.drawnCards++;
return nil;
}
The number assigned to the NSUInteger (drawnCards) in the init method is set correctly, however, I am not able to change it later. I get no warnings or crashes in Xcode, but the number remains unchangeable. I have tried to do self.drawnCards++ and self.drawnCards = 10 etc, but nothing works. Any idea what might be wrong with my code? I am checking the value with:
NSLog(#"Value: %tu", self.drawnCards);
I believe the problem here is the objectiveC dot syntax. Because you are doing . with an object this is the equivalent to doing [self drawnCards]++ method call. The result of the method is incremented, not the drawnCards ivar. To do what you want you'll either need to do _drawnCards++ (access the iVar directly), or you'll need to do [self setDrawnCards:self.drawnCards++].

Objective C: allow properties in category via custom root class

There are many questions concerning the category-properties problem.
I know some possibilities to address this:
use a singleton registry
objc_setAssociatedObject and objc_getAssociatedObject
From my point of view both is not clean since the memory allocated is never cleared when the object that created such properties is deallocated.
Categories are a good way to keep code clean and dynamically add functionality to already existing classes. They help to group functionality and to distributed implementation work among more developers.
The bad about categories is the missing storage.
I came across this problem several times now and I'm wondering whether the following would address this problem in an clean way that also takes care about the memory and if there are any problems that I can't see right now.
There is one restriction, that I can ignore since I'm working as a framework developer: I'm able to create my own root class that all my other classes can inherit from.
First of all declare the new root object:
#interface RootObject : NSObject
- (void)setRuntimeProperty:(id)runtimeProperty forKey:(id<NSCopying>)key;
- (id)runtimePropertyForKey:(id)key;
#end
With the corresponding implementation:
#import "RootObject.h"
#interface RootObject ()
#property (readwrite) NSMutableDictionary *runtimeProperties;
#end
#implementation RootObject
#synthesize runtimeProperties = _runtimeProperties;
- (id)init {
self = [super init];
if (self)
{
_runtimeProperties = [[NSMutableDictionary alloc] initWithCapacity:1];
}
return self;
}
- (void)dealloc {
[_runtimeProperties release];
_runtimeProperties = nil;
[super dealloc];
}
- (id)runtimePropertyForKey:(id)key {
return [self.runtimeProperties objectForKey:key];
}
- (void)setRuntimeProperty:(id)runtimeProperty forKey:(id<NSCopying>)key {
if (key)
{
if (runtimeProperty)
{
[self.runtimeProperties setObject:runtimeProperty forKey:key];
}
else
{
[self.runtimeProperties removeObjectForKey:key];
}
}
}
#end
By using this RootObject instead of NSObject it should be very easy to add a "property" to a category on a class. Consider having some class MyClass
#interface MyClass : RootObject
// some interface here
#end
When implementing a special behavior on top of this class you are now able to add a property like this:
#interface MyClass (specialBehavior)
#property (nonatomic, retain) NSString *name;
#property (nonatomic, copy) NSDate *birthday;
#end
With corresponding implementation:
#implementation MyClass (specialBehavior)
#dynamic name;
- (NSString *)name {
return [self runtimePropertyForKey:#"name"];
}
- (void)setName:(NSString *)name {
[self setRuntimeProperty:name forKey:#"name"];
}
#dynamic birthday;
- (NSDate *)birthday {
return [self runtimePropertyForKey:#"birthday"];
}
- (void)setBirthday:(NSDate *)birthday {
[self setRuntimeProperty:[birthday copy] forKey:#"birthday"];
}
#end
Such an implementation could KVO compatible as well by just adding the necessary calls in the setter method.
Very straight forward, but I'm wondering whether I missed something important? (E.g. very very bad runtime performance having many such declared properties or using many of these objects)
This is effectively the same as objc_setAssociatedObject and objc_getAssociatedObject, which do release memory when the object is deallocated (depending on the association type). I would guess they also have much lower overhead than your suggested code.

Object initialization style [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
Imagine my class has following ivars/properties:
#property (nonatomic, copy) NSString *itemName;
#property (nonatomic, copy) NSString *serialNumber;
#property (nonatomic) int valueInDollars;
#property NSDate *dateCreated;
1) One way to initialize ivars of this class is like this:
// Designated initializer of this class
-(id) initWithItemName: (NSString*) name
valueInDollars:(int)value
serialNumber:(NSString *)sNumber
{
// Call the superclass's designated initializer
self = [super init];
if(self)
{
// Init properties
self.itemName = name;
self.serialNumber = sNumber;
self.valueInDollars = value;
dateCreated = [[NSDate alloc] init];
}
// Return the address of the newly initialized object
return self;
}
2) Another way I am thinking go initialize this class is for example is to write:
-(id) init
{
self = [super init];
if(self)
{
// basically do nothing
}
return self;
}
And then leave it up to the user who will be using the class to do initialization as he needs it, e.g.,
MyClass *object = [[MyClass alloc] init];
object.dateCreated = [[NSDate alloc] init];
[object.dateCreated someMethod];
object.itemName = "Hello";
object.someProperty = [[SomeClass alloc] init];
The thing with above I think is that some properties (as above) must be called a alloc/init before they can be used isn't it? And if user forgets to do so, then at most the app won't work as expected right? (It won't crash as we can send message to nil). What I wrote here seems to be the only problem with this way of initialization. What is your opinion?
ps. it is permitted as here too: http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/Initialization/Initialization.html
pps. Assuming ARC is used.
thanks for many replies, but basically I was interested in what are the possible problems with solution 2?
I think you'll find the answer in the document that you linked to:
Overriding init is fine for subclasses that require no additional data to initialize their objects. But often initialization depends on external data to set an object to a reasonable initial state.
So if your class is not in a reasonable state if the variables are not initialised to a proper value, you should use
- (id)initWithItemName:(NSString*)name valueInDollars:(int)value serialNumber:(NSString *)sNumber
Then in init you could either call your designated initialiser with default values, or if there are no reasonable default values disallow the use of init as described here on SO
I'd advise you to create a factory method that calls to the init method in order to combine allocation and initialization in the same step and also hiding the details of the initialization.
#interface CCAttachment()
#property (readwrite, strong, nonatomic) NSString *urlString;
#property (readwrite, strong, nonatomic) NSString *baseURLString;
#property (readwrite, strong, nonatomic) NSData *data;
#property (readwrite, strong, nonatomic) id object;
#property (readwrite, strong, nonatomic) AFHTTPClient *client;
#end
#implementation CCAttachment
//Init method
- (id)initWithURLString:(NSString *)aURLString baseURLString:(NSString *)aBaseURLString
{
self = [super init];
if (self)
{
self.urlString = aURLString;
self.baseURLString = aBaseURLString;
}
return self;
}
//Factory method
+ (instancetype)attachmentWithURLString:(NSString *)aURLString baseURLString:(NSString *)aBaseURLString
{
return [[self alloc] initWithURLString:aURLString baseURLString:aBaseURLString];
}
#end
They will provide a more uniform interface for creating instances. For example, if you later want to convert the above object to an nsmanagedobject, you would keep the same factory method and only change its implementation
+ (instancetype)attachmentWithURLString:(NSString *)aURLString baseURLString:(NSString *)aBaseURLString
{
CCAttachment *result = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(self.class) inManagedObjectContext:MOC];
result.urlString = aURLString;
result.baseURLString = aBaseURLString;
return result;
}
http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/ClassFactoryMethods/ClassFactoryMethods.html
As #Kreiri said - Depedns... But I think that if you want to prevent some stupid errors/mistakes you should give a user a tool called designated initializer. The less possibilities to crash the better your code is!
You should try to initialize ivars by yourself to prevent any type of crash. 1st approach bounds the user to input all parameters, and is safe. 2nd approach can cause crash a you mentioned yourself, but you can be on safe side if you initialize variables yourself in 2nd approach.
-(id) init
{
self = [super init];
if(self)
{
itemName = #"";
serialNumber = #"";
valueindollars = -1;
dateCreated = nil;//Better than garbage
}
return self;
}
As a good programming practice, you should initialize objects to nil to avoid access to any garbage value.
You should make yourself familiar with the basics of Object Oriented Design.
Generally, an instance of a class SHALL fulfill a crisp defined invariant after it has been created. That means, your instance (that includes all ivars) SHALL be in a particular, logical correct state after it has been initialized.
It's also required that after responding to any message the instance MUST still fulfill its invariants. This state may be different than before responding to the message, but it MUST still in a reasonable state.
Form your second design, a user can set and read any ivar at any time through the properties. Now suppose, your instance also responds to other messages which you have defined as methods in your class.
And now answer that question: is it guaranteed that at any time an instance of your class is in a state which fulfills the invariant conditions and thus can always respond to any messages in a clear and defined manner?
You will realize, that in the second approach, this is only the case when your class just serves as a container for the four ivars, and has effectively no other responsibilities. So, one should ask what's then the purpose of the class at all? ;)
As a general principle, it is bad style to create an object with an invalid state, because the user may forget that he has to set a valid state for some variable before using it.
Sometimes there are too many possible arguments, which would make the classical pattern (sometimes called “telescoping constructor”) cumbersome. Think of pizza ingredients for example initPizzaWithPepperoni:tomate:mozzarella:...etc. For this cases, you can use a builder pattern.
This example from “Effective Java” illustrates three different ways to initialize an object
builder: useful when there are too many possible arguments or their combinations are complex.
bean
“telescoping constructor”
main.m
int main(int argc, char *argv[]) {
#autoreleasepool {
NutritionFacts *facts = [NutritionFacts new];
// builder
facts = [[[[facts builder] calories:100] sodium:35] build];
// bean constructor pattern
facts.calories=100;
facts.sodium=35;
// telescoping constructor pattern
// [[NutritionFacts alloc] initWithCalories:100];
// [[NutritionFacts alloc] initWithCalories:100 sodium:35];
}
}
NutritionFacts+Builder.m
#interface NutritionFacts(Builder)
-(NutritionFactsBuilder*)builder;
#end
#implementation NutritionFacts(Builder)
-(NutritionFactsBuilder*)builder {
return [[NutritionFactsBuilder alloc]initWithNutritionFacts:self];
}
#end
NutritionFacts.m
#interface NutritionFacts : NSObject
#property (nonatomic, assign) NSUInteger calories, carbohydrate,
cholesterol, fat, fiber, protein, saturatedFat, sodium;
#end
#implementation NutritionFacts
#end
NutritionFactsBuilder.m
#interface NutritionFactsBuilder : NSObject
-(id)initWithNutritionFacts:(NutritionFacts*)facts;
#end
#implementation NutritionFactsBuilder {
NutritionFacts *_facts;
}
-(id)initWithNutritionFacts:(NutritionFacts*)facts {
self = [super init];
if (self){
_facts = facts;
}
return self;
}
-(BOOL)isValid {
// ... check valid ivar combos
NSAssert(YES,#"Always valid");
}
-(NutritionFacts*)build {
[self isValid];
return _facts;
}
-(instancetype)calories:(NSUInteger)calories {
_facts.calories = calories;
return self;
}
-(instancetype)sodium:(NSUInteger)sodium {
_facts.sodium = sodium;
return self;
}
// ...
#end

Get Access to Object in Obj-c [duplicate]

I am using the iPhone SDK and have an issue doing something simple. I am trying to add an NSNumber object to an NSMutableArray instance variable. I tried adding NSNumber card to NSMutableArray viewedCardsArray, however without breaking, it does not get added to the array. Here is the code.
/////////////////////////////////////////////////////
// Inside the header file Class.h
#interface MyViewController : UIViewController {
NSMutableArray *viewedCardsArray;
//snip ...
}
#property (nonatomic, retain) NSMutableArray *viewedCardsArray;
#end
/////////////////////////////////////////////////////
// Inside the methods file Class.m
#import "StudyViewController.h"
#implementation StudyViewController
#synthesize viewedCardsArray
//snip ...
- (IBAction)doShowCard {
//snip ...
NSNumber *cardIdObject = [[NSNumber alloc] initWithInt:(int)[self.currentCard cardId]];
[viewedCardsArray addObject: cardIdObject];
[cardIdObject release];
}
So this code executes, and does not seem to leak (according to the Leaks performance tool). However when stepping through the code, at no point does CardIdObject appear in viewedCardsArray.
Looking through SO, I know these basic questions are pretty common to ObjC newbies (like me) so apologies in advance!
Have you initialized your viewedCardsArray? If not you need to somewhere - this is usually done in the init method for your class:
- (id)init
{
self = [super init];
if(self) {
viewedCardsArray = [[NSMutableArray alloc] init];
}
return self;
}
Then it is released in the dealloc method:
- (void)dealloc
{
[viewedCardsArray release];
[super dealloc];
}
Perspx has outlined one way of initializing the array. However, you can also use the class methods provided by NSArray:
self. viewedCardsArray = [NSMutableArray array];
This can go in init or elsewhere.
Note: The object will be autoreleased.

Resources