I do not understand how this makes sense. I put two objects in the discoveryinfo dictionary inside the MCNearbyServiceAdvertiser object that I create and the browser doesn't see the advertiser, yet when I move the second object out of the dictionary and comment it out at the end of the line, the browser sees the advertiser. Does the discoveryinfo dictionary only accept one object to work? I have a string as the first object and an array as the second. Here is what it looks like:
advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:myPeerID discoveryInfo:#{#"Name": [[NSString alloc] initWithString:myUniqueID], #"Peers": [[NSArray alloc] initWithArray:connectedPeersAry]} serviceType:#"Blahblah"];
And before that line, I tried this simpler format (but went to the above just in case the syntax was the problem):
advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:myPeerID discoveryInfo:#{#"Name": myUniqueID, #"Peers": connectedPeersAry} serviceType:#"FRCSCOUT"];
I guess I can put a dictionary or array inside the discoveryinfo dictionary, but I feel it's a pretty dumb way of doing things because a dictionary shouldn't ever be limited to one object for any case.
I'll go ahead and put my objects in another layer to "conserve space" inside the discoveryinfo dictionary, but if any of you find a better way of doing things or if you are seeing the same problem, please let me know.
You can have multiple objects in the discoveryInfo dictionary, but keep in mind that the dictionary will be encoded in a Bonjour TXT record. This imposes a few restrictions on what can be put into that dictionary.
As stated in the documentation for [MCNearbyServiceAdvertiser initWithPeer:discoveryInfo:serviceType:]:
This data is advertised using a Bonjour TXT record, encoded according to RFC 6763 (section 6). As a result:
The key-value pair must be no longer than 255 bytes (total) when encoded in UTF-8 format with an equals sign (=) between the key and the value.
Keys cannot contain an equals sign.
For optimal performance, the total size of the keys and values in this dictionary should be no more than about 400 bytes so that the entire advertisement can fit within a single Bluetooth data packet.
Well, found my problem. As quoted by Apple in their class reference for MCNearbyServiceAdvertiser:
"The content of discoveryInfo will be advertised within Bonjour TXT records, so you should keep the dictionary small for better discovery performance."
So, looks like I'll have to use a comma separated string of some sort...
EDIT
I misunderstood the Multipeer Connectivity API. I thought the roles were reversed and the Advertiser was basically the public host for a Multipeer Session, but it should be the Browser that invites Advertisers. I now just have the Unique ID generated as the discovery info.
Thank you all for your help and sorry for the API confusion on my part.
Related
I'm trying to understand the concept of NSCache, and one thing that strikes me is that a NSCache instance does not guarantee to give back the value to a key you stored before. It might not even store the key value pair when you try to add it, if it deems that the performance is more important at the moment.
What that implies, for me, is that:
Each key must 'hold' enough information to generate the value if necessary
Each query for the NSCache, which essentially is just in the form of a key, should thus wrap up all the information needed to generate the corresponding value.
From the above two points one can say that NSCache serves no purpose of establishing any kind of association between a key and a value - the user must be able to generate the value independent of the cache, and the sole purpose of using a NSCache is not to 'look up' some value, but rather just to trade memory for some performance boost
So my problem is about storing transparency masks for images. Initially I thought I just need to use the names of the images as the keys, but from my deductions above it seems that's not sufficient - I also have to include all other parameters used in generating a mask e.g. the transparency threshold, for example. It also means that every time I ask the cache for a mask I have to provide ALL the parameters. And the only way that I can think of about doing that is to use something like NSInvocation as the key; but that seems a rather clunky solution.
It is the very nature of a cache to be volatile, so caches should only ever be used to speed up access to information that could also be acquired some other way.
Your idea to create keys that hold all this information should work - just remember to store all your keys somewhere other than the cache as well.
As for the key, you can create a very simple class that has nothing but a couple of properties (the ones that make up a key), an isEqual: and hash method and maybe an initializer that takes parameters for each of your properties.
This requires extremely little code, since accessors and iVars for properties are autogenerated, so the only thing you really need to write is the isEqual: method (and hash).
This class is so small and taylor-made for the particular case you need it for, it makes sense to declare and implement it at the top of the .m file you're going to use it in. This way, you don't pollute the rest of the system. Just add #interface and #implementation sections for your class at the top of your .m file.
After more thought about this I think I've got one thing wrong - the keys in a NSCache do not necessarily need to hold all the information for generating the values. A key in a NSCache can serve the same purpose as that in a NSDictionary - a unique identifier to look up the value. The only difference, though, is that you'd always need to have a backup plan B for a NSCache in case the key-value pair added before is destroyed.
In simplier terms, operations on the two different classes look like this:
NSDictionary
generate each value V for each key K and add the pairs to the dictionary
look up V using K
NSCache
look up V using K
if V == nil, generate the value V and add the pair to the cache
Therefore it's possible to convert almost any NSDictionary to a NSCache, only that after the conversion you can't pass the NSCache around - you have to know how to generate the values at all times and thus the NSCache instance would most probably be a private property used exclusively in a certain class.
For my problem I've resolved to use a method like this (self is supposedly pointing to a subclass of NSCache, but I haven't tested it yet)
- (Mask *) maskForImageName:(NSString *)name maskGenerator:(Mask *(^)(NSString *))generator {
Mask *mask = [self objectForKey:name];
if (!mask) {
mask = generator(name);
[self setObject:mask forKey:name];
}
return mask;
}
It would be further simplified if objective-c is a functional, lazy-style language, in which case I don't even need to wrap the generator in a block; but I'm satisfied with this solution for now. In fact I feel that this pattern is almost always used with NSCache so I'd just add it as a category to NSCache.
I am sorry to ask this but I have searched for hours on doing this but I really don't understand it. Please help me. I have a .plist file in my Xcode project and it's root is a Dictionary type. It contains about 50 more dictionaries. Inside the dictionary contains strings. (Dictionary(root) > Dictionary > String. I added a search field to my toolbar and linked it to my code. I am able to get what the user types but then how do I "search" after getting what the user typed? Is there a method for this and how do I link it into my .plist? Thank you so much!!!
You want to search for the user entered string in your Dictionary of Dictionaries?
You're going to have to iterate each dictionary, asking [dict objectForKey:userEntry] in each. Not sure if you want to only find first match or all matches too.
Additionally, you may want to create an abstraction of your Dictionary of Dictionaries to reduce the scale of the problem and clarify the API. In simpler terms, wrap your Dictionary of Dictionaries in a class and put a sensible (non-dictionary-based) set of methods on it. It's probably worth the effort.
To load the plist into a Dictionary, look at [Dictionary dictionaryWithContentsOfFile].
Edit: Filtering options on NSDictionary
Have you looked at the following options for filtering values in an NSDictionary:
[NSDictionary keysOfEntriesPassingTest:] (10.6 and later) or
take the [rootDictionary allValues] NSArray and use Predicates, perhaps like this.
I have an app which shows the user a number of messages. I need to inform the server that the user has read the messages. To do this, I need to send an HTTP POST request to /api/mark_as_read/ with the IDs of the messages to mark as read, like:
ID: 1234
ID: 5678
ID: 90AB
Note that the HTTP spec allows possibly duplicate keys, which the API uses in this case.
I am currently using AFHTTPClient (part of the AFNetworking library). POST parameters are passed into AFHTTPClient as an NSDictionary, which of course has unique keys, meaning I can only pass in one value called "ID".
I've searched for a while for a solution and haven't been able to dig anything up. But I'm sure lots of people have encountered the issue before. How do I pass my HTTP client a dictionary with non-unique keys in Objective-C?
Update - I was able to resolve the issue by updating the AFNetworking library and putting an array of IDs in one NSDictionary value. (See below.)
Try passing an array for values:
NSArray *values = [NSArray arrayWithObjects:#"1234", #"5678", #"90AB", nil];
NSDictionary *params = [NSDictionary dictionaryWithObjectAndKey:values, #"ID"];
a dictionary is a set and keys are unique by 'definition'
no way this can be done with standard dict
BUT one could inherit set and just use keys array and dups array.
afnetwork would use it as a dictionary and it would provide the output you want.
they might have such a class OR you can easily write one kinda similiar
http://cocoaheads.byu.edu/code/CHDataStructures
** this is inefficient and should only be used for THIS purpose **
Short:
I need to find core data objects by a key, which holds a unique immutable array (fixed length, but chosen at runtime) of arbitrary objects (for which not only element membership, but also element order determines uniqueness). NSManagedObject however forbids overriding [isEqual:]. Now what?
Long:
I have an entity (see diagram image for entity "…Link") in my Core Data model for which I have to guarantee uniqueness based on an attribute key ("tuple"). So far so good.
The entity's unique attribute however has to be an NSArray.
And to make things a bit more difficult I neither know the class type of the tuple's elements.
Nor do I know the tuple's element count. Well, actually the count is the same for every tuple (per core data context at least), but not known before the app runs.
There must only ever be one instance of my link entity with a given tuple.
And for obvious reason only ever one tuple instance with a given array of arbitrary objects.
Whereas two tuples are to be considered equal if [tuple_1 isEqual:tuple_n] returns YES. NSManagedObject forbids the overriding of [isEqual:] and [hash] though, otherwise things would be pretty much a piece of cake.
"…Tuple" objects are created together with their array of tokens (via a convenience method) and are immutable (and so is each "…Token" and its data attribute). (think of "…Tuple" as a "…Link"'s dictionary key.)
"…Tuple" implements "- (NSArray *)tokens;", which returnes a neatly ordered array of tokens, based on the "order" keys of "…TokenOrder". (Tuples are expected to contain at most 5 elements.)
I however expect to have tens of thousands (potentially even more in some edge cases) of "…Link" objects, which I have to (frequently) find based on their "tuple" attribute.
Sadly I couldn't find any article (let alone solution) for such a scenario in any literature or the web.
Any ideas?
A possible solution I've come up with so far would be:
Narrow amount of elements to compare
by tuple by adding another attribute
to "…Tuple" called "tupleHash",
which is pre-calculated on
object creation via: Snippet 1
Query with NSPredicate for objects of matching tupleHash (narrowing down the list of candidates quite a bit).
Find "…Link" featuring given tuple in narrowed candidate list by: Snippet 1
Snippet 1:
NSUInteger tupleHash = [[self class] hash];
for (id token in self.tokens) {
tupleHash ^= [token.data hash];
}
Snippet 2:
__block NSArray *tupleTokens = someTokens;
NSArray *filteredEntries = [narrowedCandidates filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock: ^(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject.tuple.tokens isEqualToArray:tupleTokens];
}]];
(Sorry, markdown appears to oppose mixing of lists with code snippets.)
Good idea of or just insane?
Thanks in advance!
I strongly suggest that you calculate a hash for your objects and store it in your database.
Your second snippet will seriously hurt performance, that's for sure.
Update:
You don't need to use the hash method of NSArray.
To calculate the hash, you can perform a SHA1 or MD5 on the array values, concatenated. There are many algorithms for hashing, these are just two.
You can create a category for NSArray, say myHash to make the code reusable.
As recommended in a comment by Joe Blow I'm just gonna go with SQLite. Core Data simply appears to be the wrong tool here.
Benefits:
Fast thanks to SQL's column indexing
No object allocation/initialization on SELECT, prior to returning the results. (which Core Data would require for attribute checks)
Easily query link tuples using JOINs.
Easy use of SQLite's JOIN, GROUP BY, ORDER BY, etc
Little to no wrapper code thanks to EGODatabase (FMDB-inspired SQLite Objective-C wrapper)
Newbie question here. I'd like to be able to specify through data (i.e. an XML file), the appropriate Objective-C message to send. Any advice on if this is possible or how I can do this?
The next best thing, if I can't do this, would be some way to create a map object that would correlate a key (an int) with a function (I guess also a selector). Is that possible if the above isn't?
If someone could point me to some tutorial or example code as reference, that'd be great. Right now I'm doing things with a big switch statement, and I don't like it. (I'm switching on the id and in each case, explicitly calling the method relevant to the particular id.)
I love that you asked this question; too often, I see Satan's Swollen Switch Statement. It's nice to see someone wanting to using a function-table instead.
If you're OK with using a property list file (which is usually encoded in XML), this is really easy.
Just make a property list where the root element is a dictionary, which maps from some keys to some selectors.
Key Type Value
----------------------------------------------
Root Dictionary
firstKey String someSelector
secondKey String anotherSelector
Load the contents of your property list into an NSDictionary:
id path = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"plist"];
id dict = [NSDictionary dictionaryWithContentsOfFile:path];
SEL selector = NSSelectorFromString([dict objectForKey:#"firstKey"]);
if ([someObject respondsToSelector:selector]) {
[someObject performSelector:selector];
}
Of course, you'll want to refactor this logic into an appropriate method, and probably cache the property list as an instance variable.
Note: I personally think it's better to just put this function table inline; property lists are cool, but I'm not sure that it is very helpful in this case. Also, if you are cool with using Objective-C++, std::map will allow you to get away with not wrapping and unwrapping the selectors in NSString objects, etc.