I am currently pulling a list of "contest" objects that have location information tied to them. I want to monitor all contests that I am in the vicinity of.
for(Contest *contest in contests) {
NSMutableDictionary * fence1 = [NSMutableDictionary new];
[fence1 setValue:contest.event.name forKey:#"identifier"];
[fence1 setValue:[NSString stringWithFormat:#"%f", contest.location.latitude] forKey:#"latitude"];
[fence1 setValue:[NSString stringWithFormat:#"%f", contest.location.longitude] forKey:#"longitude"];
[fence1 setValue:#"1000" forKey:#"radius"];
//the below method returns a CLRegion object
CLRegion *region = [self dictToRegion:fence1];
//adding custom contest object to region
region.contest = contest;
[self.locationManager requestStateForRegion:region];
}
I ALSO want to tie a custom contest property to the region (as shown in the above code), but it doesn't seem to carry over in the delegate method didDetermineState:
-(void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
switch (state) {
case CLRegionStateUnknown:
NSLog(#"Unknown state!");
break;
case CLRegionStateInside:
NSLog(#"Inside state!");
//this comes back as null
NSLog(#"%#", region.contest.name);
break;
case CLRegionStateOutside:
NSLog(#"Outside state!");
break;
default:
break;
}
}
In the delegate method, region.contest returns as (null). Is there a better approach to take?
Appendix (my category for CLRegion):
CLRegion+Contest.h:
#import <CoreLocation/CoreLocation.h>
#import "Event.h"
#import "Contest.h"
#interface CLRegion (Loopd)
#property (nonatomic, strong) Event *event;
#property (nonatomic, strong) Contest *contest;
#end
CLRegion+Contest.m:
#import "CLRegion+Loopd.h"
#import <objc/runtime.h>
static char defaultHashKey;
#implementation CLRegion (Loopd)
#dynamic event;
#dynamic contest;
-(Event* ) event {
return objc_getAssociatedObject(self, &defaultHashKey);
}
- (void) setEvent:(Event *)event {
objc_setAssociatedObject(self, &defaultHashKey, event, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(Contest* ) contest {
return objc_getAssociatedObject(self, &defaultHashKey);
}
- (void) setContest:(Contest *)event {
objc_setAssociatedObject(self, &defaultHashKey, event, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
There are 2 problems with your category.
1. You're using same key for both properties.
2. defaultHashKey is initialized with 0 value.
objc_getAssociatedObject and objc_setAssociatedObject work mostly like setValue:forKey: and getValueForKey:. So you need to provide key which binds the value to the object. Try something like that:
#import "CLRegion+Loopd.h"
#import <objc/runtime.h>
static char* kEventKey = "kEventKey";
static char* kContestKey = "kContestKey";
#implementation CLRegion (Loopd)
#dynamic event;
#dynamic contest;
-(Event* ) event {
return objc_getAssociatedObject(self, kEventKey);
}
- (void) setEvent:(Event *)event {
objc_setAssociatedObject(kEventKey, event, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(Contest* ) contest {
return objc_getAssociatedObject(self, kContestKey);
}
- (void) setContest:(Contest *)event {
objc_setAssociatedObject(self, kContestKey, event, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
This code should work, but would be much better to create separate object for storing this association (I'm not a great fan of using runtime for this kind of purposes).
EDIT:
Your assigning value for contest multiple times in the for loop. So one of the contests (the last one) is assigned to the CLRegion object.
Related
I'd like to implement KVO for an NSArray property that is declared as readonly. The getter for this readonly property returns a copy of the private NSMutableArray that backs the backs the public readonly one:
In my .h:
#interface MyClass : NSObject
#property (readonly, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
#end
And in my .m:
#interface MyClass()
#property (strong, nonatomic) NSMutableArray *myPrivateArray;
#end
#implementation MyClass
- (NSArray *)myArray {
return (NSArray *)[self.myPrivateArray copy];
}
- (void) addObjectToMyArray:(NSObject *)obj {
[self willChangeValueForKey:#"myArray"];
[self.myPrivateArray addObject:obj];
[self didChangeValueForKey:#"myArray"];
}
- (void) removeObjectToMyArray:(NSObject *)obj {
[self willChangeValueForKey:#"myArray"];
[self.myPrivateArray removeObject:obj];
[self didChangeValueForKey:#"myArray"];
}
#end
In my tests, I am seeing an exception thrown when I call didChangeValueForKey:. Is this the correct way to do this?
I recommend that you don't use a separate property for the mutable array. Instead, have the array property backed by a mutable array variable. Then, implement the indexed collection mutating accessors and make all changes to the array through those. KVO knows to hook into those accessors and emit change notifications. In fact, it can emit better, more specific change notifications that can allow observers to be more efficient in how they respond.
#interface MyClass : NSObject
#property (readonly, copy, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
#end
#interface MyClass()
// Optional, if you want to be able to do self.myArray = <whatever> in your implementation
#property (readwrite, copy, nonatomic) NSArray *myArray;
#end
#implementation MyClass
{
NSMutableArray *_myArray;
}
#synthesize myArray = _myArray;
// If you optionally re-declared the property read-write internally, above
- (void) setMyArray:(NSArray*)array {
if (array != _myArray) {
_myArray = [array mutableCopy];
}
}
- (void) insertObject:(id)anObject inMyArrayAtIndex:(NSUInteger)index {
[_myArray insertObject:anObject atIndex:index];
}
- (void) removeObjectFromMyArrayAtIndex:(NSUInteger)index {
[_myArray removeObjectAtIndex:index];
}
- (void) addObjectToMyArray:(NSObject *)obj {
[self insertObject:obj inMyArrayAtIndex:_myArray.count];
}
- (void) removeObjectToMyArray:(NSObject *)obj {
NSUInteger index = [_myArray indexOfObject:obj];
if (index != NSNotFound)
[self removeObjectFromMyArrayAtIndex:index];
}
#end
According to the KVO docs, https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-BAJEAIEE, you need to implement automaticallyNotifiesObserversForKey, something like
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:#"myArray"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
I have not tested this code, so apologies if I am on the wrong track.
This is fragile, and - (NSArray *)myArray keeps returning different values for the same array which KVO doe not like.
You'd be better of to define a private mutable array and a public read-only array. The when you make changes to the mutable array:
self.myPublicReadOnlyArray=self.myMutableArray.copy;
That way you can avoid all the will/has changed notifications because self.myPublicReadOnlyArray is KVC/KVO compliant.
I don't have terribly much experience with this, but I post this answer in hopes that it will either solve your issue or lead you to a solution. In the past I've used this:
-(void)viewDidLoad{
[self addObserver:self forKeyPath:kYoutubeObserverKey options:NSKeyValueObservingOptionNew context:nil];
}
-(void) addLocatedYoutubeURLToList:(NSString *)youtubeURL{
// -- KVO Update of Youtube Links -- //
[self willChangeValueForKey:kYoutubeObserverKey
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:[NSSet setWithObject:youtubeURL]];
[self.youtubeLinksSet addObject:youtubeURL];
[self didChangeValueForKey:kYoutubeObserverKey
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:[NSSet setWithObject:youtubeURL]];
}
kYoutubeObserverKey corresponds to:
static NSString * const kYoutubeObserverKey = #"youtubeLinksSet";
and I use a property of the same name in this class, hence the keyvalue name:
#property (strong, nonatomic) NSMutableSet * youtubeLinksSet;
I would add an observer for your key and specify what change your interested in observing. Additionally, I'd keep your key naming consistent, meaning that if you're updating the private key, then observe that private key, not the public one. When the observer detects a change in the private key, then have your public key update as a result of that. For example:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSNumber * keyValueChangeType = change[#"kind"];
if ([keyValueChangeType integerValue] == NSKeyValueChangeInsertion) {
if ([keyPath isEqualToString:kYoutubeObserverKey] ) {
//code and such...
}
}
}
Is there a standard pattern for implementing a mutable/immutable object class pair in Objective-C?
I currently have something like the following, which I wrote based off this link
Immutable Class:
#interface MyObject : NSObject <NSMutableCopying> {
NSString *_value;
}
#property (nonatomic, readonly, strong) NSString *value;
- (instancetype)initWithValue:(NSString *)value;
#end
#implementation MyObject
#synthesize value = _value;
- (instancetype)initWithValue:(NSString *)value {
self = [self init];
if (self) {
_value = value;
}
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
Mutable Class:
#interface MyMutableObject : MyObject
#property (nonatomic, readwrite, strong) NSString *value;
#end
#implementation MyMutableObject
#dynamic value;
- (void)setValue:(NSString *)value {
_value = value;
}
#end
This works, but it exposes the iVar. Is there a better implementation that remedies this situation?
Your solution follows a very good pattern: the mutable class does not duplicate anything from its base, and exposes an additional functionality without storing any additional state.
This works, but it exposes the iVar.
Due to the fact that instance variables are #protected by default, the exposed _value is visible only to the classes inheriting MyObject. This is a good tradeoff, because it helps you avoid data duplication without publicly exposing the data member used for storing the state of the object.
Is there a better implementation that remedies this situation?
Declare the value property in a class extension. An extension is like a category without a name, but must be part of the class implementation. In your MyMutableObject.m file, do this:
#interface MyMutableObject ()
#property(nonatomic, readwrite, strong) value
#end
Now you've declared your property, but it's only visible inside your implementation.
The answer from dasblinkenlight is correct. The pattern provided in the question is fine. I provide an alternative that differs in two ways. First, at the expense of an unused iVar in the mutable class, the property is atomic. Second, as with many foundation classes, a copy of an immutable instance simply returns self.
MyObject.h:
#interface MyObject : NSObject <NSCopying, NSMutableCopying>
#property (atomic, readonly, copy) NSString *value;
- (instancetype)initWithValue:(NSString *)value NS_DESIGNATED_INITIALIZER;
#end
MyObject.m
#import "MyObject.h"
#import "MyMutableObject.h"
#implementation MyObject
- (instancetype)init {
return [self initWithValue:nil];
}
- (instancetype)initWithValue:(NSString *)value {
self = [super init];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
// Do not use the iVar here or anywhere else.
// This pattern requires always using self.value instead of _value (except in the initializer).
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
MyMutableObject.h:
#import "MyObject.h"
#interface MyMutableObject : MyObject
#property (atomic, copy) NSString *value;
#end
MyMutableObject.m:
#import "MyMutableObject.h"
#implementation MyMutableObject
#synthesize value = _value; // This is not the same iVar as in the superclass.
- (instancetype)initWithValue:(NSString *)value {
// Pass nil in order to not use the iVar in the parent.
// This is reasonably safe because this method has been declared with NS_DESIGNATED_INITIALIZER.
self = [super initWithValue:nil];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
// The mutable class really does need to copy, unlike super.
return [[MyObject allocWithZone:zone] initWithValue:self.value];
}
#end
A fragment of test code:
NSMutableString *string = [NSMutableString stringWithString:#"one"];
MyObject *object = [[MyObject alloc] initWithValue:string];
[string appendString:#" two"];
NSLog(#"object: %#", object.value);
MyObject *other = [object copy];
NSAssert(object == other, #"These should be identical.");
MyMutableObject *mutable1 = [object mutableCopy];
mutable1.value = string;
[string appendString:#" three"];
NSLog(#"object: %#", object.value);
NSLog(#"mutable: %#", mutable1.value);
Some debugging right after the last line above:
2017-12-15 21:51:20.800641-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801423-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801515-0500 MyApp[6855:2709614] mutable: one two
(lldb) po mutable1->_value
one two
(lldb) po ((MyObject *)mutable1)->_value
nil
As mentioned in the comments this requires discipline in the base class to use the getter instead of the iVar. Many would consider that a good thing, but that debate is off-topic here.
A minor difference you might notice is that I have used the copy attribute for the property. This could be made strong instead with very little change to the code.
I am learning iOS programming and am confused by the following code regarding the use of keyword self.
From my understanding, self is like Java's this. It refers to the current instance. When I want to call a class method, the usual way should be like [PlayingCard validSuits]; But it's also OK to invade a class method on an instance, right? Like [self validSuits]; (I am in the class so self refers to an instance of PlayingCard)
But in the following code, it gives error somewhere but looks ok elsewhere.(Pointed out by 3 comments, this is within Xcode 5.1)
Am I missing anything?
(P.S. I think I am having the similar problem as here, which no one answered yet. He got the same error even using [PlayingCard validSuits]. )
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[PlayingCard rankStrings] count] -1;
//1. [self rankStrings] works fine.**
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [PlayingCard rankStrings];
//2. if change rankStrings to self, then error:
//No visible #interface for 'PlayingCard' declares the selector 'rankStrings'
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[PlayingCard validSuits] containsObject:suit]) {
//3.error when changed to [self validsuits]
//No visible #interface for 'PlayingCard' declares the selector 'validsuits'**
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end
The header file:
// PlayingCard.h
#import "Card.h"
#interface PlayingCard : Card
#property (nonatomic, strong) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
#end
If you are calling another class method from inside a class method (of the same class) you can just use [self classMethod]. If however you are in an instance method and you need to call that classes class method you can use [[self class] classMethod]
As pointed out by #Martin R - if you subclass PlayingCard, calling self in a class method will then be that subclass and not PlayingCard.
EDIT:
For completeness you need to do:
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[self rankStrings] count] -1;
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [[self class] rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[[self class] validSuits] containsObject:suit]) {
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end
There are many questions concerning the category-properties problem.
I know some possibilities to address this:
use a singleton registry
objc_setAssociatedObject and objc_getAssociatedObject
From my point of view both is not clean since the memory allocated is never cleared when the object that created such properties is deallocated.
Categories are a good way to keep code clean and dynamically add functionality to already existing classes. They help to group functionality and to distributed implementation work among more developers.
The bad about categories is the missing storage.
I came across this problem several times now and I'm wondering whether the following would address this problem in an clean way that also takes care about the memory and if there are any problems that I can't see right now.
There is one restriction, that I can ignore since I'm working as a framework developer: I'm able to create my own root class that all my other classes can inherit from.
First of all declare the new root object:
#interface RootObject : NSObject
- (void)setRuntimeProperty:(id)runtimeProperty forKey:(id<NSCopying>)key;
- (id)runtimePropertyForKey:(id)key;
#end
With the corresponding implementation:
#import "RootObject.h"
#interface RootObject ()
#property (readwrite) NSMutableDictionary *runtimeProperties;
#end
#implementation RootObject
#synthesize runtimeProperties = _runtimeProperties;
- (id)init {
self = [super init];
if (self)
{
_runtimeProperties = [[NSMutableDictionary alloc] initWithCapacity:1];
}
return self;
}
- (void)dealloc {
[_runtimeProperties release];
_runtimeProperties = nil;
[super dealloc];
}
- (id)runtimePropertyForKey:(id)key {
return [self.runtimeProperties objectForKey:key];
}
- (void)setRuntimeProperty:(id)runtimeProperty forKey:(id<NSCopying>)key {
if (key)
{
if (runtimeProperty)
{
[self.runtimeProperties setObject:runtimeProperty forKey:key];
}
else
{
[self.runtimeProperties removeObjectForKey:key];
}
}
}
#end
By using this RootObject instead of NSObject it should be very easy to add a "property" to a category on a class. Consider having some class MyClass
#interface MyClass : RootObject
// some interface here
#end
When implementing a special behavior on top of this class you are now able to add a property like this:
#interface MyClass (specialBehavior)
#property (nonatomic, retain) NSString *name;
#property (nonatomic, copy) NSDate *birthday;
#end
With corresponding implementation:
#implementation MyClass (specialBehavior)
#dynamic name;
- (NSString *)name {
return [self runtimePropertyForKey:#"name"];
}
- (void)setName:(NSString *)name {
[self setRuntimeProperty:name forKey:#"name"];
}
#dynamic birthday;
- (NSDate *)birthday {
return [self runtimePropertyForKey:#"birthday"];
}
- (void)setBirthday:(NSDate *)birthday {
[self setRuntimeProperty:[birthday copy] forKey:#"birthday"];
}
#end
Such an implementation could KVO compatible as well by just adding the necessary calls in the setter method.
Very straight forward, but I'm wondering whether I missed something important? (E.g. very very bad runtime performance having many such declared properties or using many of these objects)
This is effectively the same as objc_setAssociatedObject and objc_getAssociatedObject, which do release memory when the object is deallocated (depending on the association type). I would guess they also have much lower overhead than your suggested code.
i am trying to implement the GKChallengeListener in my code and i have used GameCenterManager class from apple. the player is authenticated fine and leaderboard and challenges are also.
But i want to notify my application when the remote player completed the challenge. for this i have used GKChallengeListener protocols. but they are not calling up when i am sending or receiving the challenges reference from. but i dnt understood which class will call the delgate to self
https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKEventListener_Ref/Reference/Reference.html#//apple_ref/occ/intf/GKChallengeListener
In ViewController.h
#interface ViewController : UIViewController <UIActionSheetDelegate, GameCenterManagerDelegate,GKGameCenterControllerDelegate,GKChallengeListener> {
GameCenterManager *gameCenterManager;
NSInteger currentScore;
NSString* currentLeaderBoard;
IBOutlet UILabel *currentScoreLabel;
}
#property (nonatomic, retain) GameCenterManager *gameCenterManager;
#property (nonatomic, assign) NSInteger currentScore;
#property (nonatomic, retain) NSString* currentLeaderBoard;
#property (nonatomic, retain) UILabel *currentScoreLabel;
- (IBAction) showLeaderboard;
- (IBAction) increaseScore;
#end
In ViewController.m
#implementation ViewController
#synthesize gameCenterManager;
#synthesize currentScore;
#synthesize currentLeaderBoard;
#synthesize currentScoreLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentLeaderBoard = kLeaderboardID;
self.currentScore = 0;
if ([GameCenterManager isGameCenterAvailable]) {
self.gameCenterManager = [[GameCenterManager alloc] init];
[self.gameCenterManager setDelegate:self];
[self.gameCenterManager authenticateLocalUser];
} else {
// The current device does not support Game Center.
}
}
-(void)player:(GKPlayer *)player issuedChallengeWasCompleted:(GKChallenge *)challenge byFriend:(GKPlayer *)friendPlayer{
NSLog(#"issued challenge was completed by friend");
}
-(void)player:(GKPlayer *)player didCompleteChallenge:(GKChallenge *)challenge issuedByFriend:(GKPlayer *)friendPlayer{
NSLog(#"player did complete challenge");
}
-(void)player:(GKPlayer *)player didReceiveChallenge:(GKChallenge *)challenge{
NSLog(#"player did recieve challenge");
}
-(void)player:(GKPlayer *)player wantsToPlayChallenge:(GKChallenge *)challenge{
NSLog(#"player wants to play challenge ");
}
This is depreciated in iOS 7.0 but still you can use this as an alternative
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentLeaderBoard = kLeaderboardID;
self.currentScore = 0;
if ([GameCenterManager isGameCenterAvailable]) {
self.gameCenterManager = [[GameCenterManager alloc] init];
[self.gameCenterManager setDelegate:self];
[self.gameCenterManager authenticateLocalUser];
} else {
// The current device does not support Game Center.
}
GKChallengeEventHandler
*gk
=[GKChallengeEventHandler challengeEventHandler].delegate=self;
}
- (void)localPlayerDidCompleteChallenge:(GKChallenge *)challenge
{
NSLog(#"localPlayerDidCompleteChallenge");
}
- (void)localPlayerDidReceiveChallenge:(GKChallenge *)challenge
{
NSLog(#"localPlayerDidReceiveChallenge");
}
- (void)localPlayerDidSelectChallenge:(GKChallenge *)challenge{
NSLog(#"localPlayerDidSelectChallenge");
}
- (void)remotePlayerDidCompleteChallenge:(GKChallenge *)challenge{
NSLog(#"remotePlayerDidCompleteChallenge");
}
- (BOOL)shouldShowBannerForLocallyCompletedChallenge:(GKChallenge *)challenge
{
return YES;
}
- (BOOL)shouldShowBannerForLocallyReceivedChallenge:(GKChallenge *)challenge
{
return YES;
}
- (BOOL)shouldShowBannerForRemotelyCompletedChallenge:(GKChallenge *)challenge
{
return YES;
}
Struggled with this myself for a bit today.
The trick is to register the object that implements the GKLocalPlayerListener protocol as the listener for the localPlayer. Er... Well, that sentence seems to have looped back on itself but this might help.
/* this happens inside my authenticateLocalPlayer method */
if ([GKLocalPlayer localPlayer].authenticated) {
[[GKLocalPlayer localPlayer] registerListener:self];
// More stuff here
}
And then later on in the same object you implement the protocol methods.
Edit: Oh! And you aren't supposed to implement GKChallengeListener. You should only implement GKLocalPlayerListener. (I was going to post a link to that, but danged if I can find it now.)
Hope that helps.