Objective-c Code Blocks as nested method - objective-c

Is it possible to have an nested method in Obj-c. What I an trying to do is create a specific object when initialising an NSArray:
NSArray array = #[
^{ NSViewController someObject = ...
someObject.someObjectProperty = #"Bla";
return someObject},
^{ NSViewController someOtherObject = ...
someOtherObject.someOtherObjectProperty = #"Bla Bla";
return someObject},
];
The array contains __NSGlobalBlock__, where I am trying to get [someObject, someOtherObject]
The array is returning a stack of ViewControllers and I need to set different properties on each of them.
Thanks

Code blocks are not just "for execution sometime in the future". They can be used that way, but they can certainly be used in the way you suggest; they are a lambda. Of course, you have the syntax all wrong, because you are describing the block, not calling it. (Though an array containing blocks is also perfectly reasonable.)

If I understand you correctly, you want to create a local block and add its return value directly to the array. While it's not a mainstream method, it is possible:
NSArray *array = #[
(id)^{
id someObject = ...
someObject.aProp = #"Bla";
return someObject;
}(), // here you immediately execute the local block
(id)^{
id someObject = ...
someObject.aProp = #"Bla";
return someObject;
}()
];

Related

NSArray of NSDictionary in Objective C

I am trying to develop a function to add Dictionaries into Arrays.
This function looks this way but doesn't work:
in the implementation of the CBLModel I have the property emailList. The property I want to be an NSArray of NSDictionary's.
-(void) addEmail: (NSDictionary*) c{
NSMutableArray* mutArrayEmail= [NSMutableArray arrayWithObject: self.emailList];
[mutArrayEmail addObject:c];
self.emailList = [NSArray arrayWithObject:mutArrayEmail];
}
And the implementation would be something like:
[persInf addEmail: email1];
[persInf addEmail: email2];
[persInf addEmail: email3];
Any help?
EDIT
Thanks to everyone for your reply.
I have inserted in the main code the following (in order to not test everything easier):
NSDictionary *inventory = #{
#"Mercedes-Benz SLK250" : [NSNumber numberWithInt:13],
#"Mercedes-Benz E350" : [NSNumber numberWithInt:22],
#"BMW M3 Coupe" : [NSNumber numberWithInt:19],
#"BMW X6" : [NSNumber numberWithInt:16],
};
NSMutableArray *nsmut= [[NSMutableArray alloc] init];
[nsmut addObject: inventory];
[nsmut addObject: inventory];
[nsmut addObject: inventory];
NSArray *nsarray= [NSArray arrayWithArray:nsmut];
[persInf setEmailList:nsarray];
After this I get an error. I post this code sample as may be easier to see where the error can be
You only want to create the array if it doesn't already exist:
- (void)addEmail:(NSDictionary *) c{
if (!self.emailList) {
self.emailList = [[NSMutableArray alloc] init];
}
[self.emailList addObject:c];
}
This is lazy initialization as the array is created on first use. This is fine, as long as you realise that the array can be nil at times in the object's lifetime (which is less of a problem in Objective-C than in other languages).
A more conventional approach is to override an init method and allocate the array there.
Declare your NSMutableArray outside of your addEmail: method. You can use addObject: as-is.
Since emailList is a property you should override the getter to lazily instantiate it when its needed, like so.
- (NSMutableArray *)emailList {
if(!_emailList) {
_emailList = [NSMutableArray array];
}
return _emailList
}
To be thorough you could write that code using the ternary and make it a bit shorter:
- (NSMutableArray *)emailList {
return _emailList = _emailList ?: [NSMutableArray array];
}
Finally once you've overrode the getter your addEmail method becomes:
- (void)addEmail: (NSDictionary*)c {
[self.emailList addObject:c];
}
If you have to store emailList as an NSArray, I guess you need to set it with
self.emailList = [NSArray arrayWithArray:mutArrayEmail];
I'm far from an expert, though, so there may be problems with this approach. I'd welcome comments to that effect!
You can declare additional property of NSMutableArray (it can be in your class extension):
#property(nonatomic, copy)NSMutableArray *tempArray;
Initialize it in the init
_tempArray = [[NSMutableArray alloc] init];
And change your setter method to:
-(void) addEmail: (NSDictionary*) c{
[self.tempArray addObject:c];
}
And change your getter to:
- (NSArray *)emailList {
return [NSArray arrayWithArray:[self.tempArray copy]];
}
I see you are new to SO, so a little (friendly!) advice: you need to provide more detail in your questions of exactly what you've tried and what has gone wrong; e.g. "After this I get an error." does not help others help you.
Now reading your question and comments two things pop out: assertions about CBLModel, and how to add items to an array.
A. First, CBLModel. You've stated a few times you need an NSArray as:
I can't declare it as Mutable array or it won't work in the CBLModel
What do you mean by this? If you are saying that the methods in CBLModel take NSArray type arguments then you can pass NSMutableArray's as an NSMutableArray is an NSArray (i.e. it is a subclass) - this is one of the central pillars of inheritance-based languages like Objective-C.
If instead you mean that CBLModel does not function correctly if you pass it an NSMutableArray, say because you yourself mutate the array after passing it and that effects CBLModel in an invalid way[*], then you can simply create an immutable copy ([NSArray arrayWithArray:someMutableArray]) or just another mutable copy ([someMutableArray copy]) which you pass only to CBLModel.
B. Adding items to an array. Let's look at your code, line by line, so you can see what it is doing:
NSMutableArray* mutArrayEmail= [NSMutableArray arrayWithObject: self.emailList];
This creates a new NSMutableArray initialised with a single element obtained from self.emailList, and stores a reference to it in mutArrayEmail. E.g. if before execution self.emailList references an array [1, 2, 3], then after execution mutArrayEmail references a (mutable) array [ [1, 2, 3] ] - a single element array whose element is itself an array.
[mutArrayEmail addObject:c];
You add the object referenced by c, so using the same example you now have [ [1, 2, 3], c ] - an array with two elements.
self.emailList = [NSArray arrayWithObject:mutArrayEmail];
And finally you create a new array with the single object referenced by mutArrayEmail. Keeping the same example you now have [ [ [1, 2, 3], c ] ]. That is probably not what you wanted, or what CBLModel accepts.
The class method arrayWithArray: creates an array with the same elements as another array. If you used that method the above example would produce [1, 2, 3, c], which is probably what you want.
HTH
[*] Any classes which accept objects of types with mutable subtypes [arrays, strings, etc.] would normally be written such that a mutation after the object is passed does not effect the behaviour in undesirable ways, i.e. the classes would copy objects if needed. This it is recommended that properties of such type are defined with the copy attribute.
It's a bad idea, generally, to expose a mutable array to the outside world if it's part of an object's state. It means the object's state can be amended without the object knowing about it.
Here's how I would do this:
Have a readonly property that lets people get the array.
Have a method to add objects to the array.
#interface MyClass : NSObject
#property (nonatomic, readonly, strong) NSArray* emailList;
-(void) addEmail: (NSDictionary*) newEmail;
#end
In the implementation, have an mutable instance variable to back the property, initialise it in your designated initialiser. Implement your add method.
// All code assumed to be using ARC
#implementation MyClass
{
NSMutableArray* _emailList;
}
#synthesize emailList = _emailList;
-(id) init
{
self = [super init];
if (self != nil)
{
_emailList = [[NSMutableArray alloc] init];
}
return self;
}
-(void) addEmail: (NSDictionary*) newEmail
{
[_emailList addObject: newEmail];
}
#end
Although, the actual object returned by the property is an NSMutableArray the rule is to program to the interface, which means that when an interface says it returns an immutable object, even if you know it's really a mutable subclass, you are not allowed to send it mutating methods.
If, however, you are paranoid, implement the getter for the property like this:
-(NSArray*) emailList
{
return [_emailList copy];
}

Getting the class type for a nil object?

If I have an object that is already allocated, then doing object.class returns a non-nil value. So far so good. But, if the object has not yet been allocated, then accessing object.class returns nil.
I want to allocate an object based on its type dynamically, so for example:
#property NSArray *myArray;
...
// myArray is nil so far
self.myArray = [_myArray.class new];
However, I can't do this because _myArray.class is returning nil. So how would I determine the class type of a nil instance?
Update:
It is in fact possible. Check out my answer below.
You cannot determine the class of a nil instance, because it does not have one: it can be, quite literally, of any type derived from the type of the variable. For example, NSMutableArray is perfectly compatible with NSArray:
NSArray *myArray = [NSArray new]; // OK
NSArray *myArray = [NSMutableArray new]; // Also OK
Since the run-time capabilities of different subclasses can vary a lot, it is always up to your program to decide what kind of objects it wants.
Objective-C is a duck-typed language. This means that there are several things you can or can't do, and one of the things you can't is statically get a reference to the type of a variable.
Specifically, in your expression:
[_myArray.class new]
First, _myArray.class is evaluated, and then the result is sent the new message. Since _myArray is nil to begin with, _myArray.class returns nil as well, and the new message will return nil too, because sending any message to nil returns nil (or the closest representation to zero the return type has). This is why it doesn't work.
I suspect you come from a strongly-typed language like C#; what you're doing right now is the equivalent of Foo foo = (Foo)Activator.CreateInstance(foo.GetType()), which is sure to fail because foo.GetType() will either not compile or throw an exception (depending on if it's a class field or a local variable) since it was never assigned a value. In Objective-C, it compiles but it doesn't works. What you would want is Activator.CreateInstance(typeof(Foo)), but notice that Foo is now hardcoded here too, so you might as well just create a new Foo().
You say that the compiler "knows the type" of the object. This is not exactly true. First, NSArray and NSMutableArray are the root classes of the NSArray class cluster. This means that both are abstract, and [NSArray alloc] and [NSMutableArray alloc] return an instance of a subclass (NSCFArray last time I checked, and possibly something else; I recall seeing _NSArrayM). Maybe [NSArray new] works, but it's not giving you a plain NSArray.
Second, type safety is not enforced. Consider this code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
So even though the compiler thinks that bar is an NSArray, it's in fact a NSString. If we plug in your code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
NSArray* baz = [bar.class new];
baz is now an NSString as well. Since you ask for the runtime class of bar, the compiler has nothing to do with the operations.
And precisely because of that kind of behavior, you should probably instantiate your object with a class that you know, using [NSArray new] instead of trusting _myArray to be non-nil, and to be what you think it is.
You must init the property , or it will be nil , send a message to a nil object , it will return nil , so ,you must first init the array like _array = [[NSArray alloc] init];
So, for anyone wondering if this is possible, it is:
objc_property_t property = class_getProperty(self.class, "myArray");
const char * const attrString = property_getAttributes(property);
const char *typeString = attrString + 1;
const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
const char *className = typeString + 2;
next = strchr(className, '"');
size_t classNameLength = next - className;
char trimmedName[classNameLength + 1];
strncpy(trimmedName, className, classNameLength);
trimmedName[classNameLength] = '\0';
Class objectClass = objc_getClass(trimmedName);
NSLog(#"%#", objectClass);
Output:
NSArray
Done with the help of extobjc.
Nil has no class type
In Objective-C the actual class on an instance variable is only determined at runtime. So, you can't know the class of a nil object.
This is not an issue in your situation since you only need to do:
NSArray *myArray = [NSArray new];
Or
NSArray *myArray = [[NSArray alloc] init];
In Objective-C most decisions are deferred to the runtime
(as much as possible)
Objective-C is a runtime oriented language, which means that when it's
possible it defers decisions about what will actually be executed from
compile & link time to when it's actually executing on the runtime.
This gives you a lot of flexibility in that you can redirect messages
to appropriate objects as you need to or you can even intentionally
swap method implementations, etc.
This requires the use of a runtime
which can introspect objects to see what they do & don't respond to
and dispatch methods appropriately. If we contrast this to a language
like C. In C you start out with a main() method and then from there
it's pretty much a top down design of following your logic and
executing functions as you've written your code. A C struct can't
forward requests to perform a function onto other targets.
Source: Understanding the Objective-C Runtime

How to pass ivar into a function and set it without losing the reference to the original object

I am passing an ivar (NSMutableArray) into some method. I was expecting that if I modify the object inside the function, it would be reflected outside the function, but in this case I need to set the object; something like the following:
- (void) someMethod:(SMResponseObject *)response onData:(NSMutableArray *)imAnIvar {
imAnIvar = [response objects];
//Some other stuff
}
But I noticed that the memory reference of imAnIvar inside the function changes when I set it, and given that, the actual ivar doesn't change. I understand that the problem is that I'm changing the reference of the object inside the method, so it stops pointing to the ivar and then it points to some other random memory direction.
I thought about one solution to this problem, and it can be to ensure that the ivar is not nil before calling the function and do something like this:
- (void) someMethod:(SMResponseObject *)response onData:(NSMutableArray *)imAnIvar {
NSMutableArray *data = [response objects];
[arrayForTableView removeAllObjects];
for(id element in data){
[imAnIvar addObject:element];
}
//Some other stuff
}
So I use the original object instead of setting it directly. The problem is that in order for this to work I need to ensure that the ivar is not nil, which I think is not clean, because I'll need to do something like this on every call to the method:
if(!_ivar){
//alloc it
}
So my question is: Is there a way to force the local scope variable to point to the original variable even if I'm setting it? if not, is there any cleaner way to make this work?
Do you mean this?
- (void)setFoo:(SomeClass **)objPtr
{
*objPtr = someOtherObject;
}
// call it as:
SomeClass *foo = someObject;
NSLog(#"Before: %#", foo);
[self setFoo:&foo];
NSLog(#"After: %#", foo);
Why not use a getter for the array so that you need not check for the array being nil while using it?
-(NSMutableArray *)iAmAnIvar {
if(_iAmAnIvar == nil) {
_iAmAnIvar = [NSMutableArray array];
}
return _iAmAnIvar;
}
And when you have to set a value to the array, as you mentioned in your question, you could use
[self.iAmAnIvar removeAllObjects];
[self.iAmAnIvar addObject:someObj];
I believe you can use the - (id)copy; function of NSObject
so your code might look like this:
- (void)someFunction:(NSString *)someArg
{
NSString *str = [someArg copy];
}

correct way to allocate the NSMutableArray

I wanted to know which is the right way to allocate an NSMutableArray.
NSMutableArray *a;
Where a is a class level variable.
First method is:
self.a = [NSMutableArray alloc] init];
Second method is:
a = [NSMutableArray alloc] init];
Which method is better? Can anyone please help me out in this?
If a is a class variable, then correct way to allocate NSMutableArray will be creating a temporary array and assigning it to class variable, followed by releasing the temporary variable.
You can do this way:
NSMutableArray *temp = [[NSMutableArray alloc]init];
self.a = temp;
[temp release];
It depends on the property type. (Though it's in most cases a retain)
You should either use a temp value or create it in one string and send an autorelease message:
self.a = [[NSMutableArray alloc] init] autorelease];
You must send an autorelease becuase a property increases retain count by one. This is the same as doing:
[self setA:array];//where array - is newly created array
Where:
- (void)setA:(NSMutableArray *)array {
if (array != a) {
[a release];
a = [array retain];//here you increased a retain count by 1
}
}
You can also use an autorelease method of creation:
self.a = [NSMutableArray array];
There are several ways. But below way is good enough per me whether you are working with ARC/Non-ARC. Just make sure you have created property.
self.a = [NSMutableArray array];//It will return autorelease object.
The difference between the methods:
1) When you use self.a ,
You use the setter & getter methods created in the #synthesize.
2) When you use just a,
You bypass the accessor methods and directly modify the instance variable. (a in here).
There are two ways to look at it.
Many programmers say that you should never call the accessors from within the implementation of the object as this adds unnecessary overhead.
Some others say that you should always use the accessors, and never access the instance variable directly.
It is generally safe to use an object directly, if you are reading its value only. If you are modifying the object, you should use the accessors in order to make sure that any other objects observing that property are properly notified.
The latest objective C syntax allows you to create mutable and non-mutable arrays very quickly.
The following two examples demonstrate this:
NSArray *objectsToAdd = [#"Ted", #"Ned" , #"Sed"];
NSMutableArray *objectsToAdd = [ #[#"Ted", #"Ned" , #"Sed"] mutableCopy ];
NSMutableArray *a = [[NSMutableArray alloc] init];

Obj-c, how do I create function which will populate an NSDictionary and gain a value from the function?

I've been reading about NSArrays and NSDictionaires and I think I need the later. I'm trying to populate an object from a small database table. So I can access the string values via a record id. I have to do this several times so putting it into an object makes sense.
I have the basics...
- (void)viewDidLoad {
// WORKING START
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
[dictCategories setValue:#"Utility" forKey:#"3"];
[dictCategories setValue:#"Cash" forKey:#"5"];
NSString *result;
result = [dictCategories objectForKey:#"3"];
NSLog(#"Result=%#", result);
// WORKING END
// Can't get this bit right, current error Request for member
// 'getCategories' in something not a structure or union
NSMutableDictionary *dictCategories2 = self.getCategories;
NSLog(#"Result2=%#", [dictCategories2 objectForKey:#"5"]);
[super viewDidLoad];
}
-(NSMutableDictionary*)getCategories {
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
[dictCategories setValue:#"Utility" forKey:#"3"];
[dictCategories setValue:#"Cash" forKey:#"5"];
return dictCategories;
}
you are calling the method wrong,try [self getCategories]
You're not being clear on what isn't working, but a few things that are obviously wrong (JonLOo might be spot on though) ...
Firstly. You're using the wrong methods, or at least there's a better one -- setValue:forKey: should/could be setObject:forKey: instead. This might be one of the reasons for your issue.
Secondly. You're over-allocating and not releasing properly. dictCategories2 in your viewDidLoad will vanish into the void and bring with it the allocated memory for dictCategories defined in the getCategories method. An easy standard fix for this is to change
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
in getCategories into
NSMutableDictionary *dictCategories = [NSMutableDictionary dictionary];
It will be autoreleased using the latter method by the system.
Thirdly. You want to read up on #property. Instead of getFoo, setBar, the Ob-C standard is to use #properties to (pre)define setters and getter methods. You can then override these to populate default data into your methods when appropriate. You also (probably) want to store the dictionary in your interface as an instance variable, rather than letting it be deallocated all the time. Example of a #property implementation that does this:
#interface foo {
NSMutableDictionary *ingredients;
}
#property (nonatomic, retain) NSMutableDictionary *ingredients;
#end
// ....
#implementation foo
#synthesize ingredients;
// ...
// the #synthesize command above will create getter and setter methods for us but
// we can override them, which we need to do here
- (NSMutableDictionary *)ingredients
{
if (ingredients != nil) {
// we've already got an ingredients variable so we just return it
return ingredients;
}
// we need to create ingredients
ingredients = [[NSMutableDictionary alloc] init];
[ingredients setObject:#"foo" forKey:#"bar"]
return ingredients;
}
In the viewDidLoad method (or anywhere else where you think ingredients might not have been initialized yet), you would do e.g.
NSMutableDictionary *dict = self.ingredients;
Anywhere else you can opt to use just ingredients without self, but if it's nil, your method will never be called, and you will get nil thrown at you.
This is useful in many cases, and is necessary if we want to ever read or write the ingredients variable from outside of our class. It's outside of what you're asking about, but I brought it up because you're trying to do something similar with self.getCategories.
Hope that helps.