NSMutableArray from filterUsingPredicate error - objective-c

I am trying to return a subset of my NSMutableArray (MessageArray) with the following code. MessageArray contains an NSDictionary, one of the keys being FriendStatus. I get a strange error which I know is a DUH syntax issue. "error. void value not ignored as it ought to be".
-(NSMutableArray*)FriendMessageArray {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"FriendStatus == 1"];
NSMutableArray *filtered = [MessageArray filterUsingPredicate:predicate];
return filtered;
}

"void value not ignored as it ought to be" means that a method with a signature that starts with (void) is being used to assign a value or object to a variable. What's the signature for filterUsingPredicate? does it start with (void) ?

I'm assuming "MessageArray" is an instance variable (never name instance variables this way; you should have a property called -messages and access it with self.messages). I'l further assume that it is an NSMutableArray or else you'd be getting warnings from the compiler.
NSMutableArray -filterUsingPredicate: modifies the array itself, returning void. The method you want is -filteredArrayUsingPredicate: which returns an array. The fact that the former is a verb and the latter is a noun indicates this fact even without reading the docs. Cocoa naming is extremely consistent, which is why I mention it in the first paragraph. Pay attention to the names and you will have far fewer bugs.

Related

Bulding property paths programatically with another class's selectors. How does this work?

I have a method I that takes a variable number of #selector() values and returns a proper NSString I found on this site a while back. This is useful when building property paths for me because I don't like the idea of using strings for this and like the idea of the compiler being able to check the values the path is built from. The method is:
+(NSString *)keyPathFromSelectors:(SEL)firstArg, ...
{
NSMutableArray *keys = [NSMutableArray array];
va_list args;
va_start(args, firstArg);
for (SEL arg = firstArg; arg != nil; arg = va_arg(args, SEL))
{
[keys addObject:NSStringFromSelector(arg)];
}
va_end(args);
return [keys componentsJoinedByString:#"."];
}
This works beautifully but the question is: Why? If I have property on my current object named person and it has a name the path would be person.name obviously and the call to my method would look like:
+(NSSet *)keyPathsForValuesAffectingFoo
{
return [NSSet setWithObject:[self keyPathFromSelectors:#selector(person), #selector(name),nil]];
}
The name selector is (should not) technically visible in my current class, it's in the Person class to which I have a reference so how am I accessing the correct name SEL object?
Selectors are do not carry the guarantee that the method actually exists on the object in question. You can get compiler warnings that a selector name doesn't exist anywhere, but you won't get assurance that it does exist on the object it will be used on. For example, this compiles:
[person performSelector: #selector(applicationDidBecomeActive:)]
Even though applicationDidBecomeActive exists on your app delegate and not person.
However! This kind of thing is possible through compiler macros. Look at the libextobjc library which provides exactly the kind of selector/key path compile time checking you're looking for. This is what powers a lot of the keypath magic in ReactiveCocoa but it can also be used standalone.

Modify parameters in Objective-C blocks

I would like to modify various variables which exist outside an Objective-C block within it's body.
I know I can directly access and modify a variable using the __block attribute while declaring the variable. So this works:
__block NSMutableString *alertMessage;
void(^appendMessage)(NSMutableString*, NSString*)= ^(NSString *append){
if (!alertMessage)
{
alertMessage = [NSMutableString new];
}
if ([append length] > 0)
{
[alertMessage appendString:#"\n"];
}
[alertMessage appendString:append];
};
appendMessage(#"Alert part 1"); //This works fine
However I want to create a block which can perform an operation on a passed variable, enabling me to use the operation on multiple variables outside the block without directly accessing the same. Something like the following:
__block NSMutableString *alertMessage;
__block NSMutableString *otherString;
void(^appendMessage)(NSMutableString*, NSString*)= ^(NSMutableString *string, NSString *append){
if (!string)
{
string = [NSMutableString new];
}
if ([append length] > 0)
{
[string appendString:#"\n"];
}
[string appendString:append];
};
//The following do not work as intended
appendMessage(alertMessage, #"Alert Part 1");
appendMessage(otherString, #"Bleh bleh");
I want to be able to use the above block to modify the variables declared before it.
How can I achieve such an operation? Is this even possible?
Your question shows some confusion over values and variables, maybe the following will help.
Modify parameters in Objective-C blocks
In (Objective-)C all parameters to methods/functions/blocks are passed by value, e.g. when in the call f(x) the value of the variable x is passed to f, not the variable itself. This is known as call-by-value.
There are languages which do allow variables to be passed, known as call-by-reference. When used the argument must be a variable and the parameter name within the function is effectively an alias to the supplied variable. This is not supported directly in (Objective-)C.
However you can emulate it in (Objective-)C. It is not commonly used, with one notable exception: many methods use it to return an NSError * value.
You later comment:
What I want to achieve includes object creation, which is essentially what the question now boils down to. "Can I create an object declared outside within a block?". The answer which I have gathered with the help of all the activity here is NO.
You can, it is just a question of whether you should (i.e. is the design right?) and the best way to do it.
The straightforward way to solve your particular issue is to write a function:
NSMutableString *alertMessage;
NSMutableString *otherString;
NSMutableString *(^appendMessage)(NSMutableString *, NSString *) =
^(NSMutableString *string, NSString *append)
{
if (!string)
string = [NSMutableString new];
if (append.length > 0)
{
[string appendString:#"\n"];
[string appendString:append];
}
return string;
};
alertMessage = appendMessage(alertMessage, #"Alert Part 1");
otherString = appendMessage(otherString, #"Bleh bleh");
If you really (really, really) want to you can instead "pass the variable" by passing its address (using the & operator) and indirection (using the * operator) inside the block to get/set the value:
void (^appendMessage)(NSMutableString **, NSString *) =
^(NSMutableString **stringPtr, NSString *append)
{
if (!stringPtr) return; // no "variable" passed
NSMutableString *string = *stringPtr; // use indirection to get the value in the passed variable
if (!string)
string = [NSMutableString new];
if (append.length > 0)
{
[string appendString:#"\n"];
[string appendString:append];
}
*stringPtr = string; // use indirection to set the passed variable
};
appendMessage(&alertMessage, #"Alert Part 1"); // pass "variable" by passing its address
appendMessage(&otherString, #"Bleh bleh");
While the above is valid code it is generally not recommended coding practice in Objective-C for simple cases such as yours.
Once you take the address of a variable you need to be concerned over the lifetime of that variable - if you attempt to use the address to access the variable after the variable has been destroyed your program will fail (the dangling pointer problem)
What about __block?
Neither of the above examples use __block anywhere.
When a block references a variable by default it captures the variables value at the time the block is created. The __block attribute changes this to capturing the variable (so its value can be changed by the block) and alters the lifetime of the capture variable if required (so the variable lives at least as long as the capturing block, avoiding the dangling pointer problem).
The __block attribute is not applicable in your situation as you wish to capture different variables based on the call.
HTH
The code, as written, seems to confuse operation on object with object creation.
For clarity's sake, you should either pass in a mutable object to be manipulated or you should define a single __block variable whose value will be set by the block (and you do the logic after to figure out where that value should be stuffed).
Passing in something by reference is inherently dangerous to the point of being an anti-pattern (what happens as soon as you try to refactor the code to be asynchronous? At least in the __block case, the code after the block will see nil).
i.e.:
__block NSMutableString *foo = [sourceString mutableCopy];
doIt(#"new stuff"); // appends to `foo`
whereItShouldReallyGo = foo;

Assigning unarchived object to a different object works, why/how?

Assigning an archived dictionary object but unarchived and assigned to an array does not seem to produce an exception and the logged out data from the array is that of the dictionary and not in array format at all if it was possible to assign to a different return type, kinda link casting but instead your unarchiving and assigning directly. Why does this work? if anyone can explain it much more deeply it would bring to light a better understanding of the unarchiving process and the way it works.
NSDictionary *glossary = #{
#"Abstract class": #"some class",
#"Adopt": #"borrow",
#"archiving": #"Storing an object for later use"
};
if([NSKeyedArchiver archiveRootObject:glossary toFile:#"propertyList2.plist"] == YES)
NSLog(#"Archive success");
else
NSLog(#"Archive unsuccessfull");
NSArray *unArchiving = [NSKeyedUnarchiver unarchiveObjectWithFile:#"propertyList2.plist"];
NSLog(#"%#", unArchiving);
//for (NSString *obj in unArchiving) {
// NSLog(#"%#: %#", obj,unArchiving[obj]);
// }
Welcome to Objective-C! Due to the dynamism of the language, the type of an object reference (AKA variable) really only matters at compile time. At runtime, what the object is matters; what you thought it aught to be when you wrote the code doesn't.
The reason your code for unarchiving works is because the for (X x in Y) syntax is identical for arrays and dictionaries. Similarly, the indexing syntax (a[x]) is identical. All of that is handled at runtime, based on the type of the collection being iterated. As long as the semantics are the same, your code will run without error. Obviously, the moment you try to treat that dictionary as an array (e.g. refer to .lastObject) it will blow up.

Problem with an Array, a Property and NSLog

So i have this block of code it adds players to an NSMutableArray in my ViewController playerList. For some reason i cannot print all the playernames to the log. Am I doing something wrong? I keep getting an error that says member refrence struc objc_object is a pointer. Can anyone see what im doing wrong?
p1,p2,p3,p4 are all NSString Objects that just have the players names.
the addPlayer method creates a new player object with a property named playerName.
- (IBAction)addPlayerButton:(id)sender {
[self.playerList addObject:[self addPlayer:p1]];
[self.playerList addObject:[self addPlayer:p2]];
[self.playerList addObject:[self addPlayer:p3]];
[self.playerList addObject:[self addPlayer:p4]];
for (id element in playerList) {
NSLog(element.playerName);
}
}
for (id element in playerList) {
NSLog(element.playerName);
}
The compiler warning/error is because element is of type id and you can't use the dot syntax with object references of type id (a specific design choice when creating that feature, btw).
Fixed code:
for (Player *element in playerList) {
NSLog(#"%#", element.playerName);
}
Two (unrelated) problems fixed:
explicitly type element to be a reference to your player class (I assumed the name). This'll allow the dot syntax to work.
Use a format string with NSLog. If a player's name were ever to contain a formatting sequence -- %#, for example -- then NSLog() would try to expand the next (non-existent) argument to NSLog and your app would crash or print garbage (say, if the player's name were "Bob %f %f %f").
doesnt look like they are getting
added to the array properly
Make sure you allocate an array and assign it to playerList somewhere:
self.playerList = [NSMutableArray array];
Use this instead:
NSLog(#"%#", element.playerName);
NSLog is sort of like printf() and friends, but not exactly. You must provide a first argument that is a string literal with the format you want to use, followed by any variables represented in the format. In Objective-C, the special format %# means "use the obeject's description method to fill in a value (if there is one)." Sometimes you get a debugger-like output for an object that does not have that method, e.g. or some such, which isn't too useful of course.
In your case, assuming playerName is an NSString, you'll see it's name output if you use the format %# in NSLog's first argument.
EDIT:
You should be able to use a for statement like this:
for(Player *p in playerList) {
NSLog(#"%#", p.playerName);
}
Just because you use addObject: to add the objects doesn't mean you have to give up using the objects' type when you look at them from the array.
If in fact the objects in playerList are just NSStrings, then your loop can simply be
for(NSString *name in playerList) {
NSLog(#"%#", name);
}

How to use #encode() to get #"NSArray" in Objective-C

I'm using the runtime functions to get the type of a property (thanks to eJames for helping me to figure out this way).
The attribute string of the property looks like this:
T#"NSArray",&,Vstuff
I need to check if the property type is an array, at the moment I'm doing it like this:
- (BOOL)valueForKeyIsArray:(NSString *)key fromTagret:(id)target
{
NSString *lowerCaseKey = [self convertToKVCKey:key];
objc_property_t property = class_getProperty([target class], [lowerCaseKey UTF8String]);
NSString *propertyAttrs = [NSString stringWithUTF8String:property_getAttributes(property)];
NSString *encodedType = #"#\"NSArray\"";
NSRange range = [propertyAttrs rangeOfString:encodedType options:NSLiteralSearch];
return range.location != NSNotFound;
}
But since Apple can change the type definition string at any time, I would like to generate this #"NSArray" type string. I tried it with #encode(), but it did not work:
NSString *encodedType = [NSString stringWithUTF8String:#encode(NSArray *)];
So how can I generate this type string? Or is there a better way to check if this property attributes contain the array type?
There is no way to check this. In Objective-C source code the variables being typed as NSArray * is only there for the compiler to issue warnings. It has no meaning, and does not exist at runtime. If you mis-typed an NSArray as an NSString, you would get lots of warnings when compiling, but your code would behave exactly the same when run. At runtime all that is known is that the ivar/property is "an object".
Another way to think of it, is that once Objective-C is compiled, all object references are id references.
Just accept that if the runtime changes, your code will break, and move on. However, I think you might be miscategorizing ivars of type NSMutableArray *, CFArrayRef, or CFMutableArrayRef. You also seem to be assuming all keys correspond directly to a declared property.
The cleanest solution might be to assert that the sample object being used for the test (the target) must have a non-nil value for that key, and just grab the value and test that [[target valueForKey:key] isKindOfClass:[NSArray class]].