Access to NSUInteger property - objective-c

I've some problem with accessing NSUInteger property in my code, which look like that:
MyController.h
#interface MyController : UIViewController
#property (retain, nonatomic) NSArray *updatesArray;
#property (nonatomic) NSUInteger madeUpdatesCounter;
#property (nonatomic) NSUInteger allUpdatesCounter;
#end
MyController.m
#implementation MyController
#synthesize updatesArray;
#synthesize madeUpdatesCounter;
#synthesize allUpdatesCounter;
- (void)viewDidLoad
{
....
madeUpdatesCounter = 0;
allUpdatesCounter = [updatesArray count];
....
// Register for progress notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(makeProgressChange)
name:#"MadeOneUpdateNotification"
object:nil];
}
- (void)makeProgressChange
{
madeUpdatesCounter++;
NSLog(#"Update progress: %d/%d", madeUpdatesCounter, allUpdatesCounter);
}
#end
I'm prosessing my updates as NSInvocationOperation by adding to NSOperationQueue. In the end of the one update action I'm sending notification:
[[NSNotificationCenter defaultCenter] postNotificationName:#"MadeOneUpdateNotification" object:nil];
After executing above code, receiving notifocation is execute only once and in logs I see someting like this:
Update progress: 1/3
When I change line:
allUpdatesCounter = [updatesArray count];
to
allUpdatesCounter = 3;
then everything works ok, and I see in logs:
Update progress: 1/3
Update progress: 2/3
Update progress: 3/3
Variable updatedArray is initialized before view is loaded. Is done in this way:
MyController *doUpdatesVC = [self.storyboard instantiateViewControllerWithIdentifier:#"MyController"];
doUpdatesVC.updatesArray = updatesArray;
[self presentViewController:doUpdatesVC animated:YES completion:nil];
Do you have any advices or hints what I'm doing wrong in my code?

OK, I've found reason of my problems. The applications locked by accessing to the same variable by operations launched from queue. When I changed logic of my code, then everything start working correctly.

Related

NSSoundDelegate not being called

I am updating some legacy objective C code to be able to be compiled under OSX 10.13. The legacy code worked and most of the update code does as well except for an NSSoundDelegate that needs to handle a didFinishPlaying function. The delegate method is not being called. The delegate method is contained in a class called MyClass. Here is relevant code.
In MyClass.h:
#class MyClass;
#protocol MyClass <NSObject>
#optional
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)flag;
#end
#interface MyClass : NSObject <NSSoundDelegate>
{
}
#property (nonatomic, assign) id <NSSoundDelegate> delegate;
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)flag;
- (id) init;
#end
Then in MyClass.m:
#implementation MyClass
#synthesize delegate;
- (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)flag
{
if (flag) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"handleNSSoundDidFinishPlaying" object:sound];
}
}
- (id)init
{
MyClass *thePointer;
self = [super init];
if (self) {
thePointer = self;
self.delegate = (id)thePointer;
isInitialized = NO;
isClosing = NO;
[self set_currentSounds:[NSMutableArray arrayWithCapacity:0]];
}
return self;
}
#end
Can anyone see what I'm missing?
I think you should notify the delegate object like:
if([_delegate respondsToSelector:#selector(sound: didFinishPlaying:)])
[_delegate sound:self didFinishPlaying:_flag];
Hope this will help you.
Found the problem! When allocating the sound to be played, you have to set the sounds delegate using [theSnd setDelegate:self]; so that when the sound stops playing, the delegate gets called, in this case the delegate is in the MyClass .m file.

way to update wkinterfacecontroller during runtime in watch os 2

In Header file
#import <WatchKit/WatchKit.h>
#import <Foundation/Foundation.h>
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController : WKInterfaceController<WCSessionDelegate>
- (IBAction)lastSongButtonClick;
- (IBAction)playSongButtonClick;
- (IBAction)nextSongButtonClick;
#property (strong, nonatomic) IBOutlet WKInterfaceLabel *songTitleLabel;
#property (strong, nonatomic) IBOutlet WKInterfaceButton *playSongButton;
#end
So I implemented the WCSessionDelegate and every time I receive about the UI, I would want it to update. So in my .m file I have:
- (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)message{
NSString* type = [message objectForKey:#"type"];
if([type isEqualToString:#"UIUpdateInfo"]){
NSLog(#"Watch receives UI update info");
[self handleUIUpdateInfo:[message objectForKey:#"content"]];
}
}
AND
- (void)handleUIUpdateInfo:(NSDictionary*)updateInfo{
[self.songTitleLabel setText:[updateInfo objectForKey:#"nowPlayingSongTitle"]];
[self.playSongButton setBackgroundImage:[updateInfo objectForKey:#"playButtonImage"]];
}
However, it doesn't seems to update. Is there any proper way to update?
You're halfway there. You've configured receiving the message on the watch side correctly, but you'll need to trigger a message to be sent when the UI is updated (therefore triggering didReceiveMessage to execute and update the appropriate content).
Where ever you are making changes to the UI, you'll need to include this:
NSDictionary *message = //dictionary of info you want to send
[[WCSession defaultSession] sendMessage:message
replyHandler:^(NSDictionary *reply) {
//handle reply didReceiveMessage here
}
errorHandler:^(NSError *error) {
//catch any errors here
}
];
Also, make sure you're activating the WCSession properly. This is usually done in viewDidLoad or willAppear depending on whether you're implementing this on the phone or the watch.
- (void)viewDidLoad {
[super viewDidLoad];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
}
You can see a full example of an end-to-end Watch to iPhone data transfer in this tutorial - http://www.kristinathai.com/watchos-2-tutorial-using-sendmessage-for-instantaneous-data-transfer-watch-connectivity-1

Objective-C methods not running

I'm messing around with using objects to launch background threads, however when I call an objects method to call the method that will spawn a background thread, nothing happens. I'm a bit puzzled as to why, and it looks like the -init function isn't even being called. Anyways, here's what I have:
ViewController.h
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
#property(nonatomic, strong) Thread* threadedObject;
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "Threader.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_threadedObject = [[Threader alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender {
NSLog(#"Clicked.");
[_threadedObject RunInBackground];
}
#end
Threader.h
#import <Foundation/Foundation.h>
#interface Threader : NSObject
#property(nonatomic) bool IsFinishedRunning;
#property(nonatomic) bool IsThreading;
//Constructor and Destructor
-(id)init;
-(void)dealloc;
-(void)RunInBackground;
-(void)WaitForTenSeconds;
#end
Threader.m
#import "Threader.h"
#implementation Threader
//constructor
-(id)init{
[super init];
if(self != nil)
{
_IsFinishedRunning = NO;
_IsThreading = NO;
}
return self;
}
//destructor
-(void)dealloc{
[super dealloc];
}
//Runs a thread in the background
-(void)RunInBackground{
NSLog(#"Initiating thread...");
[self performSelectorInBackground:#selector(WaitForTenSeconds) withObject:nil];
}
//Waits for 10 seconds, then sets IsFinishedRunning to YES
-(void)WaitForTenSeconds{
NSLog(#"Starting to run in the background.");
_IsThreading = YES;
sleep(10);
_IsFinishedRunning = YES;
NSLog(#"Finished running in the background.");
}
#end
When I run the program, this is my output(I clicked the button a few times)
2013-05-17 15:30:57.267 ThreadedObjects Clicked.
2013-05-17 15:30:59.003 ThreadedObjects Clicked.
2013-05-17 15:30:59.259 ThreadedObjects Clicked.
2013-05-17 15:30:59.443 ThreadedObjects Clicked.
2013-05-17 15:30:59.675 ThreadedObjects Clicked.
I should be getting messages telling me that the Threader object was created, and that it is preparing to launch a background thread, that the thread has been spawned and then after 10 seconds, that the thread is done running.
So, where's my glaring obvious error?
init isn't a constructor, it's for setup after construction. You need the class object to create an instance before you can send init, and, most importantly, you need to assign the results to your variable.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
threadedObject = [[Threader alloc] init];
}
You can't send alloc to an object that's not a class; instances don't respond to it. The only reason that this isn't crashing is that globals are initialized to 0/NULL/nil, and [nil someMessage] does nothing.
Not assigning the results to your variable is the same as:
int x = 0;
x + 10;
There's no change to x's value.
Additionally, you don't seem to have an ivar there, just a global variable. Ivars need to go into a curly-brace block at the head of the #implementation:
#implementation Threader
{
Threader * threadedObject;
}
// etc...
You never alloc the object.............
Also, this is curious:
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
Threader* threadedObject;
Where exactly did you declare the threadedObject? Like above? Use an iVar
or, better, a property for it!
A couple of reactions:
Show us where your definition and alloc/init of threadedObject.
I'm not sure what business problem you're trying to solve, but this smells like the precursor of some custom NSOperation solution. Operation queues are ideally suited for these sorts of implementations.
I'd be inclined to subclass NSOperation when trying to do something like this. See the custom NSOperation object in the Concurrency Programming Guide.
I'd suggest using camelCase for your method and variable names.
If you say with this, I'd steer you away from the "thread" name, as it might imply that you're doing something with NSThread, which you're not.

KVO with Run-to-Completion semantics - Is it possible?

I recently ran into reentrancy issues with KVO. To visualize the problem, I would like to show a minimal example. Consider the interface of an AppDelegate class
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic) int x;
#end
as well as its implementation
#implementation AppDelegate
- (BOOL) application:(__unused UIApplication *)application
didFinishLaunchingWithOptions:(__unused NSDictionary *)launchOptions
{
__unused BigBugSource *b = [[BigBugSource alloc] initWithAppDelegate:self];
self.x = 42;
NSLog(#"%d", self.x);
return YES;
}
#end
Unexpectedly, this program prints 43 to the console.
Here's why:
#interface BigBugSource : NSObject {
AppDelegate *appDelegate;
}
#end
#implementation BigBugSource
- (id)initWithAppDelegate:(AppDelegate *)anAppDelegate
{
self = [super init];
if (self) {
appDelegate = anAppDelegate;
[anAppDelegate addObserver:self
forKeyPath:#"x"
options:NSKeyValueObservingOptionNew
context:nil];
}
return self;
}
- (void)dealloc
{
[appDelegate removeObserver:self forKeyPath:#"x"];
}
- (void)observeValueForKeyPath:(__unused NSString *)keyPath
ofObject:(__unused id)object
change:(__unused NSDictionary *)change
context:(__unused void *)context
{
if (appDelegate.x == 42) {
appDelegate.x++;
}
}
#end
As you see, some different class (that may be in third-party code you do not have access to) may register an invisible observer to a property. This observer is then called synchronously, whenever the property's value has changed.
Because the call happens during the execution of another function, this introduces all sort of concurrency / multithreading bugs although the program runs on a single thread. Worse, the change happens without an explicit notice in the client-code (OK, you could expect that concurrency issues arise whenever you set a property...).
What is the best practice to solve this problem in Objective-C?
Is there some common solution to regain run-to-completion semantics automatically, meaning that KVO-Observation messages go through an event-queue, AFTER the current method finishes executing and invariants / postconditions are restored?
Not exposing any properties?
Guarding every critical function of an object with a boolean variable to ensure that reentrancy is not possible?
For example: assert(!opInProgress); opInProgress = YES; at the beginning of the methods, and opInProgress = NO; at the end of the methods. This would at least reveal those kind of bugs directly during runtime.
Or is it possible to opt out of KVO somehow?
Update
Based on the answer by CRD, here is the updated code:
BigBugSource
- (void)observeValueForKeyPath:(__unused NSString *)keyPath
ofObject:(__unused id)object
change:(__unused NSDictionary *)change
context:(__unused void *)context
{
if (appDelegate.x == 42) {
[appDelegate willChangeValueForKey:#"x"]; // << Easily forgotten
appDelegate.x++; // Also requires knowledge of
[appDelegate didChangeValueForKey:#"x"]; // whether or not appDelegate
} // has automatic notifications
}
AppDelegate
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:#"x"]) {
return NO;
} else {
return [super automaticallyNotifiesObserversForKey:key];
}
}
- (BOOL) application:(__unused UIApplication *)application
didFinishLaunchingWithOptions:(__unused NSDictionary *)launchOptions
{
__unused BigBugSource *b = [[BigBugSource alloc] initWithAppDelegate:self];
[self willChangeValueForKey:#"x"];
self.x = 42;
NSLog(#"%d", self.x); // now prints 42 correctly
[self didChangeValueForKey:#"x"];
NSLog(#"%d", self.x); // prints 43, that's ok because one can assume that
// state changes after a "didChangeValueForKey"
return YES;
}
What you are asking for is manual change notification and is supported by KVO. It is a three stage process:
Your class overrides + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey returning NO for any property you wish to defer notifications for and deferring to super otherwise;
Before changing a property you call [self willChangeValueForKey:key]; and
When you are ready for the notification to occur you call [self didChangeValueForKey:key]
You can build on this protocol quite easily, e.g. it is easy to keep a record of keys you have changed and trigger them all before you exit.
You can also use willChangeValueForKey: and didChangeValueForKey with automatic notifications turned on if you directly alter the backing variable of a property and need to trigger KVO.
The process along with an examples is described in Apple's documentation.

NSOperation with repeat option

I've developed a Category that gives the NSOperation the ability to be executed in the background at timed intervals. I would really appreciate getting some feedback on this, especially any potential problems with this approach that I'm not thinking of.
Thank you!
Here's the code:
NSOperation+Repeat.h
#import <Foundation/Foundation.h>
#interface NSOperation (repeat)
#property (readonly, nonatomic) NSTimeInterval repeatInterval;
#property (readonly, nonatomic) NSOperationQueue *repeatOperationQueue;
- (void)performUsingOperationQueue:(NSOperationQueue *)operationQueue;
- (void)performAtRepeatingInterval:(NSTimeInterval)interval usingOperationQueue:(NSOperationQueue *)operationQueue;
#end
NSOperation+Repeat.m
#import "NSOperation+repeat.h"
#import <objc/runtime.h>
static char const * const RepeatPropertiesKey = "RepeatProperties";
#implementation NSOperation (repeat)
#dynamic repeatInterval;
#dynamic repeatOperationQueue;
static NSString * RepeatIntervalKey = #"interval";
static NSString * RepeatOperationQueueKey = #"operationQueue";
static NSString * RepeatTimerKey = #"timer";
- (NSMutableDictionary *)repeatProperties {
NSMutableDictionary * properties = objc_getAssociatedObject(self, RepeatPropertiesKey);
if (properties == nil) {
properties = [NSMutableDictionary new];
objc_setAssociatedObject(self, RepeatPropertiesKey, properties, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return properties;
}
- (NSTimeInterval)interval {
NSNumber * interval = [[self repeatProperties] objectForKey:RepeatIntervalKey];
return [interval doubleValue];
}
- (NSOperationQueue *)repeatOperationQueue {
NSOperationQueue * operationQueue = [[self repeatProperties] objectForKey:RepeatOperationQueueKey];
return operationQueue;
}
- (void)performUsingOperationQueue:(NSOperationQueue *)operationQueue {
[operationQueue addOperation:[self copy]];
}
- (void)performAtInterval:(NSTimer *)timer {
[self performUsingOperationQueue:self.repeatOperationQueue];
}
- (void)performAtRepeatingInterval:(NSTimeInterval)interval usingOperationQueue:(NSOperationQueue *)operationQueue {
// Save interval and operationQueue in repeatProperties
[self.repeatProperties setValue:[NSNumber numberWithDouble:interval] forKey:RepeatIntervalKey];
[self.repeatProperties setValue:operationQueue forKey:RepeatOperationQueueKey];
// Create timer to call performAtInterval on self
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:(interval*60)
target:self
selector:#selector(performAtInterval:)
userInfo:nil
repeats:YES];
// Save the timer in repeatProperties
[self.repeatProperties setValue:timer forKey:RepeatTimerKey];
[self performUsingOperationQueue:operationQueue];
}
#end
Here's an example of a NSOperation subclass that can repeat:
MSScheduleImportOperation.h
#import <Foundation/Foundation.h>
#import "NSOperation+Repeat.h"
#interface MSScheduleImportOperation : NSOperation <NSCopying>
#property (readonly, strong, nonatomic) NSString* employeeId;
- (id)initWithEmployeeId:(NSString *)employeeId;
#end
MSScheduleImportOperation.m
#import "MSScheduleImportOperation.h"
#implementation MSScheduleImportOperation
#synthesize employeeId = __employeeId;
- (id)initWithEmployeeId:(NSString *)employeeId {
self = [super init];
__employeeId = [employeeId copy];
return self;
}
- (id)copyWithZone:(NSZone *)zone {
MSScheduleImportOperation* copy = [[MSScheduleImportOperation alloc] initWithEmployeeId:self.employeeId];
return copy;
}
- (void)main
{
...
}
#end
The Apple documentation says:
An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again.
So the first problem is that there might be internals that stop it from working. Although, I see you try to get around the problem by making a copy.
This leads us to the other problem is that NSOperation is not advertised to conform to NSCopying.
[operationQueue addOperation:[self copy]];
This line should throw an exception.
Instead of a category on NSOperation where the object copies itself and adds the copy to an NSOperationQueue - it would be simpler to manage this at a higher level. For example:
+[RepeatingOperation operationBlock:(InitOperationBlock)operationBlock
queue:(NSOperationQueue*)queue
interval:(NSTimeInterval)interval];
where InitOperationBlock would be a block where the operation was created and configured.
The main benefit is the API would be harder to mess up. For example in category in the original post, performUsingOperationQueue: will fail silently if you forget to set repeatOperationQueue.