IOS: Subclassing objects with replacing - ios

I have class Project. I need to create class CheckboxProject that will be used with checkbox list, it will have property 'checked' and I will check/uncheck items.
So, I use inheritance and did it. But dataSource array is contains Project classes. How can I convert this array to CheckboxProject classes?
#interface Project : NSObject
#property (nonatomic, copy) NSString *name;
#end
#interface CheckboxProject : Project
#property (nonatomic) BOOL checked;
#end
I have dataSourse array of Project. I need to convert it to array of CheckboxProject and then I need be able to change checked to YES/NO.
I need to get array of CheckboxProject from array of Project

Array in obj-c isnt type safe so you can put what ever you want inside.
You can check at runtime the class type
id obj = [array objectAtIndex:index];
if([obj isKindOfClass:[CheckmarkProject class]]) {
// do something with the checkmark
} else if ... // the other type of class {
}
but in any case it is bad practice to do such a thing. (I would prefer accessing the base class vars in the mix case)

Related

Custom object to NSMutableDictionary

I would like to add a custom object to NSDictionary. It should store three variables: two strings and a boolean.
I read around the net and found NSCoder to be the way but I dislike the result. By using [dictionary setObject:[NSKeyedArchiver archivedDataWithRootObject:customObject]] I end up with NSData information instead of human readable text. My target is to make it readable for human eyes. I do not want to encode the object into binary data.
To make it a little bit more complicated I would like to add my objects inside NSArray. For testing purposes I tried to add NSStrings to NSArray and to invoke [NSDictionary dictionaryWithObject:array forKey:#"myKey"]. The result is perfect. It is readable for human. I would like to add my custom object instead of the NSString.
Desired result should look something like this:
{
TextStrokeColor = "UIDeviceWhiteColorSpace 0.5 1";
TextStrokeWidth = 0;
MyObjects = (
MyCustomObject = {
name = "name";
boooool = 0;
description = "";
}
, ... other objects );
}
What should I use? I do not really get the difference and the use for NSCoder, NSCoding, NSCopying.
I need to be able to edit the text file later on the disk. By having binary representation I cannot. But seems there is no straightforward method.
You can simply use arrays, dictionaries, strings, numbers, dates (anything which can be written into a plist or JSON).
The question is wether this is done solely during 'archiving', or whether your in-memory representation is also arrays and dictionaries. You can also create a custom class which either uses a dictionary internally to store the data and archives / reloads from that dictionary, or the custom class is a standard class with properties and creates a dictionary on-the-fly when archiving or reloading.
Note that when using a keyed archiver, it can support setting the outputFormat to NSPropertyListXMLFormat_v1_0 for some use cases, so once your custom class implements archiving to plist data types you can easily archive the container to a plist (JSON will require more leg work from you to collate the data into true containers).
Have you tried actually creating a custom object by just making a class?
#interface MyCustomClass : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) BOOL boolValue;
#property (nonatomic, strong) NSString *description;
#end
#implementation MyCustomClass
// put any implementation methods here
#end
Then you can add it to an NSArray or an NSDictionary as you would with any other class.
MyCustomClass *myObject = [MyCustomClass new];
myObject.name = #"name";
myObject.boolValue = YES;
myObject.description #"a description";
[myMutableArray addObject:myObject];
myMutableDictionary[#"some key"] = myObject;

how to pass the variable in segue

i have defined variable in GroupView.h
#interface GroupView()
{
NSMutableArray *chatrooms;
}
#end
#implementation GroupView
Now i want to pass this variable in segue
#interface FriendsViewController ()
#end
#implementation FriendsViewController
else if ([segue.identifier isEqualToString:#"showGroupView"]) {
GroupView *groupView = (GroupView *)segue.destinationViewController;
groupView.chatrooms = [NSMutableArray arrayWithArray:chatrooms];
}
i know that chatrooms has to be property in header file to code this way but it is not
So is there any way to use this variable in segue.
Thanks for help.
chatrooms defined as an ivar like you have done is accessed using -> notation:
groupView->chatrooms = [NSMutableArray arrayWithArray:chatrooms]
This is generally discouraged, though. You should use a property instead:
#interface GroupView
#property (strong) NSMutableArray *chatrooms;
#end
Incidentally, if you're using an NSMutableArray, that indicates that you want to modify the element list of the array directly and not just replace the array wholesale. If you only ever want to replace the array with a whole new array every time, I suggest using NSArray instead.
Another point to make here is that you're attempting to cast the object held at segue.destinationViewController as a GroupView. You have either named a UIViewController subclass in a very misleading way, or you are not accessing the GroupView as a correct member of the UIViewController that is returned to you.
Normally, if you are not building the SDK or something. You don't really have a better reason not to expose it in the header file. However, you can expose the property in the extension and declare a private property in the host class(It's really not able to pass if you just declare a local variable). For example, You have a extension called GroupView+Helper. So, you can pass it into the property exposed in the extension. And then internally translate into the GroupView.
In GroupView.m:
#interface GroupView
#property (strong, nonatomic) NSMutableArray *chatrooms;
#end
In GroupView+Helper.h
#property (strong, nonatomic) NSMutableArray *internalChatrooms;
Also, you need to import the GroupView+Helper in the GroupView.
It will make your chatrooms private and internalChatrooms protected.

Lazy Instantiation not working iOS

I've declared a property in the .h file called cellTitles. In my .m file, I have a method as follows:
-(NSArray *)cellTitles
{
if(!_cellTitles){
_cellTitles = [[NSArray alloc] initWithObjects:#"several strings", nil];
NSLog(#"Home Array Created");
}
return _cellTitles;
}
But the array is not created when I refer to _cellTitles or self.cellTitles. I have several NSLogs that all say the array has 0 objects. Do I need more than this. Some answers have said I need to synthesize, but as I understand it, that is no longer necessary.
Make sure your property is assigned strongly.
#property (nonatomic, strong) NSArray *cellTitles;

Add #propertys to Core Data class

I made few classes via Core Data. And I need some additional #propertys for one of that classes in runtime. This #propertys are responsible for download progress and I don't want to store them in Core Data DB. I tried to use a separate extension class:
#interface MyClass ()
{
CGFloat _downloadProgress;
NSInteger _downloadErrorCounter;
BOOL _downloadAllStuff;
BOOL _downloadUserCanceled;
}
#property (nonatomic, assign) CGFloat downloadProgress;
#property (nonatomic, assign) NSInteger downloadErrorCounter;
#property (nonatomic, assign) BOOL downloadAllStuff;
#property (nonatomic, assign) BOOL downloadUserCanceled;
#end
But private variables are not visible out of MyClass, and #propertys compile all right, but in runtime i get -[MyClass setDownloadErrorCounter:]: unrecognized selector sent to instance.
Can anyone suggest me some solution?
The easiest solution (if you don't want to modify the Xcode generated class files) is to add the properties to the Core Data model and define the
properties as transient. Transient properties are not saved to the store file.
Another option is to use a tool like "mogenerator", which generates two class files for each
entity, one for the Core Data properties (which is overwritten if the the model changes),
and one for your custom properties (which is not overwritten).
Update: Starting with Xcode 7, Xcode creates both a class and
a category for each managed object subclass, compare NSManagedObject subclass property in category. Custom properties can be added to the class
definition which is not overwritten when the model changes.
Just add
#synthesize downloadErrorCounter = _downloadErrorCounter;
...
in #implementation. Note, not #dynamic.
When trying to use the #synthesize solution i got the error:
#synthesize not allowed in a category's implementation.
Solution was to use associated objects as described in this blog: http://kaspermunck.github.io/2012/11/adding-properties-to-objective-c-categories/
MyManagedObject+Additions.h
#property (strong, nonatomic) NSString *test;
MyManagedObject+Additions.m
NSString const *key = #"my.very.unique.key";
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, &key, test, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)test
{
return objc_getAssociatedObject(self, &key);
}

Conditionally make a #property strong or weak

I am downloading a list of objects from an API to display to a user. The list has a mix of two types of objects. Imagine that they are combined books and authors, and the class definitions look like this:
#interface Book : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) Author *author;
#end
#interface Author : NSObject
#property (nonatomic, strong) NSString *fullName;
#property (nonatomic, weak) Book *book;
#end
Every Book can download its Author information from the API, and vice versa.
If the API gives me a Book, I can set its author property once I download it. The Author object points back to the Book through the book property, but this doesn't create an ARC Retain Cycle because the book property is weak.
However, if the API gives me an Author first, and I download its Book, the object will be deallocated once the method in which I set it returns, because the same property is weak.
I thought of a few ways around this:
Create a Content object that stores both (not viable for many-to-many relationships)
Create separate strongBook and weakBook properties, and then make a readonly property called book which checks which is set and returns that one
Those both seem messy to me, although the second option is preferable.
Is there a way to dynamically change a property from weak to strong (and vice-versa) using the Objective-C runtime?
UPDATE: I'm getting a few suggestions on how to work around the issue, which I don't have trouble coming up with myself. This question is specifically about whether there is a way to either (a) dynamically redefine #properties for a specific instance of a class, or (b) override ARC's retain/release behavior in specific circumstances (since this issue wouldn't exist in MRC).
Just a shot in the dark, but you could create the property and not specify and then use dynamic with the runtime apis. I didn't test it, but i think it should work:
//.h file
#import <Foundation/Foundation.h>
#interface SomeObject : NSObject
#property(nonatomic) NSObject *object;
#end
//.m file
#import "SomeObject.h"
#import <objc/runtime.h>
#implementation SomeObject
#dynamic object;
-(void)setObject:(NSObject *)object
{
BOOL isWeak = NO;
if(isWeak)
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
}
else
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_RETAIN);
}
}
-(NSObject *)object
{
return objc_getAssociatedObject(self, "object");
}
#end
For the period of the download, create a mutable dictionary to temporarily store author objects that arrive prior to the book. When a book is received, look in that array and see if the author info is there, if so attach it. When you are finished clean out the mutable array.

Resources