Can't access properties of returned objects? - objective-c

//SomeObject.h
#import <Foundation/Foundation.h>
#interface SomeObject : NSObject {
}
#property NSInteger aProperty;
#end
//main.m
#import <Foundation/Foundation.h>
#import "SomeObject.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:[[[SomeObject alloc] init] autorelease] forKey:#"key1"];
[dictionary setObject:[[[SomeObject alloc] init] autorelease] forKey:#"key2"];
[dictionary objectForKey:#"key1"].aProperty = 5; //Error HERE
[dictionary release];
[pool drain];
return 0;
}
But on that line XCode gives me these errors:
error: Semantic Issue: Member reference type 'struct objc_object *' is a pointer; maybe you meant to use '->'?
error: Semantic Issue: No member named 'aProperty' in 'struct objc_object'
Can't I access a property of a returned object? (I mean, without directly calling the setter method)

You need to cast the returned object:
((SomeObject*)[dictionary objectForKey:#"key1"]).aProperty = 5;
or:
[(SomeObject*)[dictionary objectForKey:#"key1"] setAProperty: 5];
or:
SomeObject* obj = [dictionary objectForKey:#"key1"];
obj.aProperty = 5;

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;
}

concatenate Strings from a NSArray?

#import <Foundation/Foundation.h>
#include <stdlib.h>
#include "GenerarPassword.h"
#implementation GenerarPassword
-(NSDictionary*) generarDiccionario
{
NSDictionary* m_Dict =[NSDictionary dictionaryWithObjectsAndKeys:
#"Dx", #"1",
#"Om", #"2",
#"Al", #"3",
#"Na", #"4",
#"Je", #"5",
#"Ko", #"6",
#"Ke", #"7",
#"Fi", #"8",
#"Re", #"9",
#"Me", #"10",
#"Mu", #"11",
#"Ra", #"12",
#"Lu", #"13",
#"Lo", #"14",
#"Ka", #"15"
,nil];
return m_Dict;
}
-(void) printPassword:(int) password
{
NSLog(#"%d",password);
}
/*Se genera la clave numérica*/
-(int) generarClave
{
srand(time(0));
int r = rand() %(9999-1000+1) +1000;
return r;
}
//Esta función Genera el valor Aleatorio
-(NSString*) GenerarValor:(NSString*) key
{
NSString *valor = [[self generarDiccionario] valueForKey: key];
return valor;
}
-(NSArray*) generarlistaletras:(int) numero
{
NSArray* lista = [[NSArray alloc] initWithObjects: [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0],nil];
return lista;
}
-(void) imprimirArreglo:(NSArray*) arreglo
{
int i = 0;
NSString *str1 =#" ";
for (i=0;i<2;i++)
{
[str1 stringByAppendingString:[arreglo objectAtIndex:0]];
}
NSLog(#"%#",str1);
}
// lista = [0,0,0]
//lista[random.randrange(0,3)] = [GenerarValor(numero)]
//for i in range(len(lista)):
// if lista[i] == 0:
// lista[i] = [GenerarValor(random.randrange(11,20))]
// return lista
#end
int main(int argc, char const *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
GenerarPassword *Generar1 = [[GenerarPassword alloc]init];
int clave = [Generar1 generarClave];
[Generar1 printPassword:clave];
NSDictionary* dict = [Generar1 generarDiccionario];
NSLog(#"%#",[[Generar1 generarDiccionario] valueForKey:#"1"]);
[Generar1 imprimirArreglo:[Generar1 generarlistaletras:123]];
[pool drain];
return 0;
}
My idea is to print the array (the function imprimirArreglo), but when run the program I get this exception: Uncaught exception NSInvalidArgumentException, reason: Can not determine type information for -[NSIntNumber (null)]; what i wanted was to print the array with format 0 0 0.
OK, I edited your code into something more sensible.
#import <Foundation/Foundation.h>
#include "GenerarPassword.h"
#interface GenerarPassword ()
#property (nonatomic, strong) NSArray *generarArray;
#end
#implementation GenerarPassword
- (NSArray *)generarArray
{
if (!_generarArray) {
_generarArray = #[#"Dx", #"Om", #"Al", #"Na", #"Je", #"Ko", #"Ke", #"Fi", #"Re", #"Me", #"Mu", #"Ra", #"Lu", #"Lo", #"Ka"];
}
return _generarArray;
}
- (void)printPassword:(NSInteger)password
{
NSLog(#"%#", #(password));
}
/*Se genera la clave numérica*/
- (NSInteger)generarClave
{
return arc4random_uniform(9000) + 1000;
}
//Esta función Genera el valor Aleatorio
- (NSString *)generarValor:(NSInteger)key
{
return self.generarArray[key];
}
- (NSArray *)generarlistaletras:(NSInteger)numero
{
return #[#0, #0, #0];
}
- (void)imprimirArreglo:(NSArray *)arreglo
{
NSString *string1 = [NSString stringWithFormat:#"%#", [arreglo componentsJoinedByString:#" "]];
NSLog(#"%#", string1);
}
#end
int main(int argc, char const *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
GenerarPassword *generarPassword = [[GenerarPassword alloc] init];
NSInteger clave = [generarPassword generarClave];
[generarPassword printPassword:clave];
NSLog(#"%#", generarPassword.generarArray[0]);
[generarPassword imprimirArreglo:[generarPassword generarlistaletras:123]];
[pool drain];
return 0;
}
There are a few things I noticed.
First, if you're going to store value next to keys that are just counting numbers... then use an array not a dictionary.
Second, you're never actually calling the method generarValor so a lot of the code doesn't seem to be doing anything.
Third, you should really be using a property to store the array/dictionary in. You are repeatedly creating new dictionaries in your code.
The line NSDictionary* dict = [Generar1 generarDiccionario]; is actually just wasting processor cycles because you're not actually using dict at all.
Fourth, variable names and methods should start with a lowercase letter, class name start with an uppercase letter.
Fifth, there is a lot of useless code here. I think I've fixed the problem, in your imprimirArreglo method though.
Sixth, format your code nicely when you put it onto StackOverflow. Pretend you're doing and exam for college. No one here is paid so the chance they'll struggle through code that's all over the place is very small.
Also, take a look through the code. I've made some improvements and changes to it to make it Objective-C code and not C++ code.

fatal error on NSArray.h

I have ubuntu machine and compiling objective-c using GNUStep. I wrote the following code:
#import <objc/objc.h>
#import <Foundation/Foundation.h>
#import <objc/NSArray.h>
int main ( int argc, char ** argv)
{
int ar[100] = {0};
int i;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *arr = [[NSArray alloc] initWithObjects:#"stackOverflow", #"1", #"2", nil];
NSLog (# "Counts in the array %i", [arr count]);
#try {
NSString *str;
str = [arr objectAtIndex:1];
NSLog (#" String value is %# ", str);
}
#catch (NSRangeException * excep)
{
NSLog (#"Reached Range caught for %#:%#" [excep name], [excep reason]);
}
[pool release];
}
But I get the following fatal error:
fatal error: objc/NSArray.h: No such file or directory
I tried <NSArray.h> also, but getting same error.
Which path I have to provide?
The file objc/NSArray.h does not exist, hence the fatal error.
Remove the #import <objc/NSArray.h> as NSArray should already be available via the Foundation import.
See https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html

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.

Set readonly attribute in ObjC

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;
}