Set readonly attribute in ObjC - objective-c

Is there a way to set a value to readonly attribute in Objective-C?
I actually don't care how nasty the code is unless it isn't stable anymore.

Never mind my comment, here's the two ways you do it:
#interface Grimley : NSObject
#property (readonly, copy) NSString * blabber;
#property (readonly, copy) NSString * narwhal;
- (id) initWithBlabber:(NSString *)newBlabber;
#end
#implementation Grimley
#synthesize blabber;
#synthesize narwhal = unicorn;
- (id) initWithBlabber:(NSString *)newBlabber {
self = [super init];
if( !self ) return nil;
// Any object can of course set its own ivar regardless
// of how the property it backs is declared.
blabber = [newBlabber copy];
// Refer to the _ivar_, not the property.
unicorn = #"One horn";
return self;
}
#end
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Grimley * g = [[Grimley alloc] initWithBlabber:#"Excelsior"];
// This is how you get around the property.
[g setValue:#"Nimitz" forKey:#"blabber"];
// Again, use the name of the variable, not the property
[g setValue:#"Pearly horn" forKey:#"unicorn"];
NSLog(#"%#", [g blabber]);
NSLog(#"%#", [g narwhal]);
[g release];
[pool drain];
return 0;
}

Related

Why weak property of associated object is not nilled out if I call its getter?

Though it's kind of stupid in 2020 that I'm still asking question about ObjC, please be patient and considerate...
I'm reading the source code of BloksKit and ran into a weird situation.
#import <objc/runtime.h>
#interface _WeakAssociatedObjectWrapper : NSObject
#property (nonatomic, weak) id object;
#end
#implementation _WeakAssociatedObjectWrapper
#end
#interface NSObject (AddWeak)
#end
#implementation NSObject (AddWeak)
- (void)setWeakProp:(id)weakProp {
_WeakAssociatedObjectWrapper *wrapper = objc_getAssociatedObject(self, #selector(weakProp));
if (!wrapper) {
wrapper = [[_WeakAssociatedObjectWrapper alloc] init];
objc_setAssociatedObject(self, #selector(weakProp), wrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
wrapper.object = weakProp;
}
- (id)weakProp {
id value = objc_getAssociatedObject(self, _cmd);
if ([value isKindOfClass:_WeakAssociatedObjectWrapper.class]) {
return [(_WeakAssociatedObjectWrapper *)value object];
}
return value;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
{
NSObject *prop = [[NSObject alloc] init];
[obj setWeakProp:prop];
[obj weakProp]; // *Weird!!
}
NSLog(#"Now obj.weakProp = %#", [obj weakProp]);
}
return 0;
}
This code is adding a weak associated object for category.(BlocksKit does so)
Note the *Weird!! line. If this line is commented out, then it prints (null), which is reasonable since prop is deallocated outside the {} scope. On the other side, if not commented out, it prints <NSObject: 0xxxxx>, which indicates that prop is somehow retained by someone(Or any other reason?).
What is happening here??! (BlocksKit behaves the same!)
Environment: XCode 10.3
This is a feature. For the case (and any similar)
[obj weakProp];
by properties/accessors naming convention ARC returns autoreleased instance, so in your case #autoreleasepool holds it and testing as below can show this.
int main(int argc, const char * argv[]) {
NSObject *obj = [[NSObject alloc] init];
#autoreleasepool {
{
NSObject *prop = [[NSObject alloc] init];
[obj setWeakProp:prop];
[obj weakProp]; // *Weird!!
}
NSLog(#"Now obj.weakProp = %#", [obj weakProp]);
}
NSLog(#"After autoreleased >> obj.weakProp = %#", [obj weakProp]);
return 0;
}

Get NSMutableDictionary from Singleton?

I created a singleton class in order to share an object inside my program. Here's the code:
SelectedRow.h
#import <Foundation/Foundation.h>
#import "TableEntry.h"
#interface SelectedRow : NSObject {
TableEntry *rowValue;
}
#property (nonatomic, retain) TableEntry *rowValue;
+ (id)sharedManager;
- (void)setVariable:(TableEntry*)value;
#end
and SelectedRow.m
#import "SelectedRow.h"
#import "TableEntry.h"
#implementation SelectedRow
#synthesize rowValue;
+ (id)sharedManager {
static SelectedRow *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
rowValue = [[TableEntry alloc] init];
}
return self;
}
- (void)setVariable:(TableEntry*)value {
rowValue = value;
}
#end
while TableEntry.h
#import <Foundation/Foundation.h>
#interface TableEntry : NSObject {
#private
NSString *videoId;
NSString *videoCategory;
NSString *videoTitle;
NSString *videoDescription;
NSDate *videoDate;
NSMutableArray *videoRelatedVideos;
NSDictionary *videoAdditionalInformation;
NSString *videoAccessControl;
NSArray *videoFields;
NSMutableDictionary *days;
NSMutableDictionary *views;
NSMutableDictionary *watchtime;
NSMutableDictionary *subscribers;
NSMutableDictionary *shares;
}
#property (copy) NSString *videoId;
#property (copy) NSString *videoCategory;
#property (copy) NSString *videoTitle;
#property (copy) NSString *videoDescription;
#property (copy) NSMutableArray *videoRelatedVideos;
#property (copy) NSDictionary *videoAdditionalInformation;
#property (copy) NSArray *videoFields;
#property (copy) NSString *videoAccessControl;
#property (copy) NSDate *videoDate;
#property (copy) NSMutableDictionary *days;
#property (copy) NSMutableDictionary *views;
#property (copy) NSMutableDictionary *subscribers;
#property (copy) NSMutableDictionary *shares;
#property (copy) NSMutableDictionary *watchtime;
- (id)setId:(NSString*)Id setCategory:(NSString*)Category setDate:(NSDate*)date setTitle:(NSString*)title setDescription:(NSString*)description setRelatedVideos:(NSMutableArray*)relatedVideos setAdditionalInformation:(NSDictionary*)additionalInformation setAccessControl:(NSString*)accessControl setFields:(NSArray*)fields setDays:(NSMutableDictionary*)days setViews:(NSMutableDictionary*)views setSubscribers:(NSMutableDictionary*)subscribers setShares:(NSMutableDictionary*)shares setWatchtime:(NSMutableDictionary*)watchtime;
- (NSString*)extractId;
- (NSString*)extractCategory;
- (NSString*)extractTitle;
- (NSString*)extractDescription;
- (NSMutableArray*)extractRelatedVideos;
- (NSDictionary*)extractAdditionalInformationVideos;
- (NSDictionary*)extractAccessControlVideos;
- (NSArray*)extractFields;
- (NSMutableDictionary*)extractDays;
- (NSMutableDictionary*)extractViews;
- (NSMutableDictionary*)extractSubscribers;
- (NSMutableDictionary*)extractShares;
- (NSMutableDictionary*)extractWatchtime;
#end
and TableEntry.m
- (id)init {
self = [super init];
if (self) {
videoId = #"9bZkp7q19f0";
videoCategory = #"Music";
videoTitle = #"Demo Title";
videoDescription = #"Demo description";
videoDate = [NSDate date];
videoAdditionalInformation = [NSDictionary alloc];
videoRelatedVideos = [NSMutableArray alloc];
videoAccessControl = #"demo accesControl";
videoFields = [NSArray alloc];
days = [NSMutableDictionary alloc];
views = [NSMutableDictionary alloc];
shares = [NSMutableDictionary alloc];
subscribers = [NSMutableDictionary alloc];
watchtime = [NSMutableDictionary alloc];
}
return self;
}
- (id)setId:(NSString*)Id setCategory:(NSString*)Category setDate:(NSDate*)date setTitle:(NSString*)title setDescription:(NSString*)description setRelatedVideos:(NSMutableArray*)relatedVideos setAdditionalInformation:(NSDictionary*)additionalInformation setAccessControl:(NSString*)accessControl setFields:(NSArray*)fields setDays:(NSMutableDictionary*)Days setViews:(NSMutableDictionary*)Views setSubscribers:(NSMutableDictionary*)Subscribers setShares:(NSMutableDictionary*)Shares setWatchtime:(NSMutableDictionary*)Watchtime {
videoId = Id;
videoCategory = Category;
videoDate = date;
videoTitle = title;
videoDescription = description;
videoRelatedVideos = relatedVideos;
videoAccessControl = accessControl;
videoAdditionalInformation = additionalInformation;
videoFields = fields;
days = Days;
views = Views;
subscribers = Subscribers;
watchtime = Watchtime;
shares = Shares;
return self;
}
- (NSString*)extractId {
return self.videoId;
}
- (NSString*)extractCategory{
return self.videoCategory;
}
- (NSString*)extractTitle{
return self.videoTitle;
}
- (NSString*)extractDescription{
return self.videoDescription;
}
- (NSMutableArray*)extractRelatedVideos{
return self.videoRelatedVideos;
}
- (NSString*)extractAccessControlVideos{
return self.videoAccessControl;
}
- (NSDictionary*)extractAdditionalInformationVideos{
return self.videoAdditionalInformation;
}
- (NSArray*)extractFields{
return self.videoFields;
}
- (NSMutableDictionary*)extractDays{
return self.days;
}
- (NSMutableDictionary*)extractSubscribers{
return self.subscribers;
}
- (NSMutableDictionary*)extractWatchtime{
return self.watchtime;
}
- (NSMutableDictionary*)extractShares{
return self.shares;
}
- (NSMutableDictionary*)extractViews{
return self.views;
}
#end
I can extract any values from the singleton with:
SelectedRow *selectedRow = [SelectedRow sharedManager];
NSString *videoID = [selectedRow.rowValue extractId];
the problem arises with any NSMutableDictionary. If I try:
SelectedRow *selectedRow = [SelectedRow sharedManager];
NSMutableDictionary *days = [selectedRow.rowValue extractDays];
or with any other NSMutableDictionary I get this error:
[NSMutableDictionary count]: method sent to an uninitialized mutable dictionary object
what I'm I doing wrong? Thanks
The [NSMutableDictionary alloc] call allocates the space for NSMutableDictionary, but it does not initialize it.
Replace it with [NSMutableDictionary dictionary] to fix the problem. Same goes for your NSArray and NSMutableArray objects (replace them with [NSMutable array] and [NSMutableArray array]).
The videoAdditionalInformation of type NSDictionary should be initialized to nil, though, because NSDictionary objects are immutable. If you are planning to set it to some dictionary later on, you might as well keep it nil on initialization.
In addition, you should reconsider the use of copy: it makes sense for NSString objects, but it hardly makes sense on NSMutableDictionary objects.

Pass data between objects

I have a variable in a class, NSNumber. I want to pass the value of this var to another class var. The problem is that I release the object of the first class and obtain an error message when I try to set the value of the second class var.
In C++ this is so easy to do. But here with memory management and pointers confused me so much.
Solution code, for testing:
#import <Foundation/Foundation.h>
#interface A : NSObject
{
NSNumber *a;
}
#property (nonatomic, retain) NSNumber *a;
#end
int main(int argc, char *argv[])
{
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
A *instance1 = [[A alloc] init];
A *instance2 = [[A alloc] init];
[instance1 setA:[NSNumber numberWithFloat:5.43f]];
instance2.a = [instance1.a copy];
[instance1 release];
NSLog(#"Valor de la que sigue viva, parte2: %#", instance2.a);
[instance2 release];
[p release];
[pool drain];
return 0;
}
You should use a retain property or copy the instance variable:
#interface A {
NSNumber *a;
}
#property (nonatomic, retain) NSNumber *a;
#end
...
A *instance1 = [[A alloc] init];
A *instance2 = [[A alloc] init];
instance1.a = instance2.a;
//or
instance2.a = [instance1.a copy];
Read some docs about retain-counted memory management which is what Objective-C uses.

Not sure why I have "EXC Bad Access" error

I've programmed for a while in Java and .Net, but never really used C or Objective C. I'm still trying to understand a few concepts. I was working on a simple program just to see how I can make an array of structures. Which I believe I got right. I'm having a hard time figuring out how to access the subclasses and store values to the subclasses I created.
I'm guessing I'm getting the error because of my use of scanf. Can anyone offer any help?
Here's what I have so far.
#import <Foundation/Foundation.h>
//Player Prototype: Stores name and wins so far. It can also print out the name and wins
#interface Player : NSObject
{
NSString *name; //Player name
NSInteger wins; //Player wins
NSInteger losses; //Player losses
NSInteger bp; //extra value for anything I might need in the future.
}
#property (retain, nonatomic) NSString *name;
#property NSInteger wins;
#property NSInteger losses;
#property NSInteger bp;
#end
//Next part
#implementation Player
#synthesize name;
#synthesize wins;
#synthesize losses;
#synthesize bp;
#end
//Brackets
#interface Bracket : NSObject
{
NSMutableArray *playerarray;
Player *addplayer;
}
#property (retain, nonatomic) NSMutableArray *playerarray;//array of players
#property (retain, nonatomic) Player *addplayer;//player and data
-(void) SetUp;
#end
//Starting Bracket, working with only 8. Later moving up to 32
#implementation Bracket
#synthesize playerarray;
#synthesize addplayer;
-(void) SetUp;//sets up the array
{
int i;//counting fun!
playerarray = [[NSMutableArray alloc] init];//initialize a bracket
for(i = 0; i < 8; i++)//To add the players
{
Player *addplayerx = [Player new];//New instance of Player
NSString *p;//Not sure if I need two of them.
NSString *tempname = #"bye";
NSLog(#"Player %d Name:", i);
scanf("%s",&p);
tempname = p;
NSLog(#"%s", tempname);
addplayerx.name = p;
NSLog(#"%s", addplayerx.name);
addplayerx.wins = 0;
addplayerx.losses = 0;
addplayerx.bp = 0;
[playerarray addObject: addplayerx];
[addplayerx release];
[p release];
}
}
#end
//End function
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Bracket *starting = [Bracket new];
[starting SetUp];
[pool drain];
return 0;
}
You can't scanf() into an NSString. You need to scan into a regular C string (make sure you allocate memory for it), and then you can construct the NSString from that using stringWithUTF8String:, or something along those lines.
Don't guess: run the application under the debugger, and when it crashes, examine the backtrace. You can also look at the backtraces in ~/Library/Logs/DiagnosticReports/foo.crash.
What are you trying to do, read data line-by-line from a file? It would be much easier to just use text = [NSString stringWithContentsOfFile:path] then split text on all newline characters:
NSCharacterSet *newlines = [NSCharacterSet newlineCharacterSet];
NSArray *lines = [text componentsSeparatedByCharactersInSet:newlines];
You can then just loop across and grab the player names:
NSMutableArray *players = [NSMutableArray arrayWithCapacity:[lines count]];
NSString *whitespace = [NSCharacterSet whitespaceCharacterSet];
for (NSString *line in lines) {
NSString *name = [line stringByTrimmingCharactersInSet:whitespace];
Player *player = [[[Player alloc] init] autorelease];
player.name = name;
[players addObject:player];
}

NSArray assignment

I have two viewControllers and one has to pass an array to another, but on receiving side i am always getting count 0. here is what i did
classA.h
Class A : UIViewController
{
#private
NSMutableArray *_array;
}
#property (nonatomic, retain ) NSMutableArray *array;
#end
classA.m
#implementation
#synthesis array =_array;
-(void) nowShow
{
int objCount = [ _array count ]; // This is always coming as 0 though i tried various ways (listed below )
}
#end
classB.m
-(void) method:(id)sender {
NSMutableArray *msgArray = [[NSMutableArray alloc] initWithCapacity:1];
for ( int i = 0 ; i <objCount; i++ ){
unsigned int idMsg = msgId[i];
[msgArray addObject:[NSNumber numberWithUnsignedInt:idMsg]];
}
classA *classAController = [[classA alloc] initWithStyle:UITableViewStylePlain];
//[ classAController.array arrayWithObject
//[classAController.array addObjectsFromArray:msgArray];
[ classAController.array initWithArray:msgArray];
//[classAController.array setArray:moveArray];
[self presentModalViewController:classAController animated:YES];
}
Any suggestion guys
You shouldn't call any init method unless it's immediately after calling alloc.
With properties, all you need to use is assignment:
classAController.array = msgArray;
The keyword should be #synthesize not #synthesis
edit: also you want to synthesize the property "array", not the instance variable _array