Reputation: 31
As I said, brand new to Objective-C. I have a few years of experience in programming, though, and I'm a quick learner.
General Issue: I want to listen to a variable inside of a different file, then perform a certain action in response to changes to that variable.
Specific Issue: I want to modify the way that the iPhone displays WiFi signal. Currently it has 4 states, but I want to make it have 8 states. My hope is to somehow listen to (or periodically read from) the variable wifiSignalStrengthRaw
in SBStatusBarDataManager.h
and then display it.
Question: What's the best way to go about reading, or listening to, that variable?
EDIT: Secondary Question: Is there a way to analyze code already compiled in Obj-C?
Upvotes: 1
Views: 1233
Reputation: 31045
Key Value Observing is a great feature, but it doesn't help you (directly) with this particular problem. For those not familiar with the class he's referring to, it's a private class within the iOS SpringBoard itself. So, he doesn't have control over how it publishes the data he's interested in.
You can't observe just any old data. It has to be coded to be Key Value Observing (KVO) compliant. If you look at the SBStatusBarDataManager.h (this is the iOS 4 version ... generate the one you need with class-dump
) ... you'll see that it's not coded that way :(
But, you could use some dynamic Objective-C runtime feature to get at the data anyway. See here on getting at private/protected instance variables directly.
Then, just locally declare a struct to match what's in the springboard header, and do this:
// this was coded to match the iOS 5.0 header, but of course, this may
// change with each iOS version
typedef struct {
char itemIsEnabled[23];
char timeString[64];
int gsmSignalStrengthRaw;
int gsmSignalStrengthBars;
char serviceString[100];
char serviceCrossfadeString[100];
char serviceImages[3][100];
char operatorDirectory[1024];
unsigned int serviceContentType;
int wifiSignalStrengthRaw;
int wifiSignalStrengthBars;
unsigned int dataNetworkType;
int batteryCapacity;
unsigned int batteryState;
char notChargingString[150];
int bluetoothBatteryCapacity;
int thermalColor;
unsigned int thermalSunlightMode:1;
unsigned int slowActivity:1;
unsigned int syncActivity:1;
char activityDisplayId[256];
unsigned int bluetoothConnected:1;
unsigned int displayRawGSMSignal:1;
unsigned int displayRawWifiSignal:1;
} SbStatusBarDataType;
a helper to retrieve ivars by name:
#import <objc/runtime.h>
- (void *) instanceVariableForObject: (id)obj andKey: (NSString *)key {
if (key != nil) {
Ivar ivar = object_getInstanceVariable(obj, [key UTF8String], NULL);
if (ivar) {
return (void *)((char *)obj + ivar_getOffset(ivar));
}
}
return NULL;
}
and finally, get the data like so:
// get an instance to the data manager this way, or however you're
// doing it via Mobile Substrate
SBStatusBarDataManager* mgr = [SBStatusBarDataManager sharedDataManager];
SbStatusBarDataType data = *(SbStatusBarDataType*)[self instanceVariableForObject: mgr andKey: @"_data"];
int signalStrength = data.wifiSignalStrengthRaw;
You could then just repeatedly query this data, at some interval that you consider fast enough.
Otherwise, try looking at the methods in SBStatusBarDataManager.h. It looks like some of them might conceivably be called at the precise time that signal strength changes. If you hook those methods, you might be able to push a notification that the data has changed, so that you don't have to constantly poll for the data.
For example:
- (void)_dataChanged;
- (void)_updateSignalStrengthItem;
- (void)_signalStrengthChange;
all look like good candidates for hooking, if you're trying to determine when there's been a change to WiFi signal strength. But, I have no experience with those, and it'll be trial-and-error on your part. Good luck!
A couple references pertaining to your Secondary question:
Apple docs on Obj-C runtime APIs
Upvotes: 2
Reputation:
What you really want is Key-Value Observing. No need to hack around with NSNotificationcenter, etc...
Upvotes: 3
Reputation: 8608
Check out NSNotificationCenter. This will let you add listeners to variables, when the variable changes it post to notification center. You can then "hear" those changes in any class.
Apple doc description of NotificationCenter:
An NSNotificationCenter object (or simply, notification center) provides a mechanism for broadcasting information within a program. An NSNotificationCenter object is essentially a notification dispatch table.
Quick example:
Add a listener
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(showMainMenu:)
name:@"loginComplete" object:nil];
Post a notification to loginComplete
[[NSNotificationCenter defaultCenter] postNotificationName:@"loginComplete" object:nil];
Hear the notification.
- (void)showMainMenu:(NSNotification *)note {
NSLog(@"Received Notification - Someone seems to have logged in");
}
Upvotes: 1
Reputation: 1081
Use a NSNotificationCenter to send values to/from different classes. Here is a good example of using the NSNotifications Send and receive messages through NSNotificationCenter in Objective-C?
Upvotes: 1
Reputation: 28541
If you are the one making the changes to the variable you want to listen, you should use the observer pattern.
Here is a tutorial to get you started: Patterns in Objective-C: Observer Pattern
And also have a look at that question on SO: What's the nicest way to do observer/observable in objective-c (iphone version)
Upvotes: 0