How can I view the contents of an objective C Protocol? - ios

If I have access to an objective-C Protocol and trying to figure out how to look inside it to see what methods it contains, including their signatures, etc.
I've tried NSLog and looking in the object in the debugger, as well as on the internet, and cannot find any way to do this.

I checked out the methods in objc/runtime.h after seeing the answers to this SO post: List selectors for Objective-C object and found a way to NSLog a protocol's method signatures
#import <objc/runtime.h>
Protocol *protocol = #protocol(UITableViewDelegate);
BOOL showRequiredMethods = NO;
BOOL showInstanceMethods = YES;
unsigned int methodCount = 0;
struct objc_method_description *methods = protocol_copyMethodDescriptionList(protocol, showrequiredMethods, showInstanceMethods, &methodCount);
NSLog(#"%d required instance methods found:", methodCount);
for (int i = 0; i < methodCount; i++)
{
struct objc_method_description methodDescription = methods[i];
NSLog(#"Method #%d: %#", i, NSStringFromSelector(methodDescription.name));
}
free(methods)
The only catch is that in protocol_copyMethodDescriptionList you need to specify whether you want required vs. non-required methods and whether you want class vs. instance methods. So to cover all four cases, you would need to call protocol_copyMethodDescriptionList four times and print the results for each list.

Related

iOS How do I find Strings in Apple's private APIs

Let's skip the discussion about using the private APIs, it's just for testing something.
I am looking for a way to access hidden field of MPMediaItem by using the value(forProperty:) but the String I'm looking for is not available publicly. I have looked over MPMediaItem header here and I could not find what I was looking for however this didn't surprise me because when looking at MPMediaPlaylist.h I also couldn't find any info about "parentPersistentID" or "isFolder" properties both which can be easily accessed by value(forProperty: "isFolder") as Bool
Normally you would access Playlist's name or other fields by something like value(forProperty: MPMediaPlaylistPropertyName), because MPMediaPlaylistPropertyName is publicly available, MPMediaPlaylistPropertyIsFolder is not so we have to use "isFolder"
Is it possible to find those hidden Strings? I am wondering if it's a matter of luck when guessing the name or there is a header which contains those Strings
Below are those String which are available publicly
I think that you really need to rethink your application in this point.
I think that one should never play with private APIs, it is made private for a purpose.
But this Objective-C code, could display the private variables:
unsigned int o;
Ivar *d = class_copyIvarList([MPMediaItem class], &o);
for(int i=0;i<o;i++) {
NSLog(#"%#", [NSString stringWithCString:ivar_getName(d[i]) encoding:NSUTF8StringEncoding]);
}
free(d);
You also may need the properties, sometimes there are computed properties (sorry for the swifty term), these properties also may be private and you may want to know it.
You can also try to add this:
objc_property_t *prop = class_copyPropertyList([something class], &l);
for (int i = 0; i < l; i++) {
NSLog(#"%#", [NSString stringWithCString: property_getName(prop[i]) encoding:NSUTF8StringEncoding]);
}

C-Style 2D Array as ivar

In C, we could do the following to create a 2D array:
int intArray[10][10];
In C99, we could create a VLA:
size_t col = 10;
size_t row = 10;
int array[row][col];
Within a method in Objective-C, I can create a 2D array that hold ids as follows:
id genObjectArray[10][10];
Is it possible to create an 2d array ivar in Objective-C?
The following is what I have tried:
#interface myClass ()
{
id objArray[][];
//This doesn't work, unless I specific size.
//I want to do this, so that I could specific the size later during
//runtime
}
In C, I could do the following and allocate space for a 2D array later within a block scope:
int **array;
int *elements;
I can do the same within Objective-C, too, but the problem arises when I use id or other object types; other words, the following is not valid:
id **array;
id *elements;
Thus, my question is, is it possible to declare a C-style 2D array as ivar that holds ids?
I understand that we could achieve that using normal NS(Mutable)Array; but this just serves for educational purposes.
You can't do this. For a C99 VLA, the space required for the array is allocated at the point the array is declared. For an ivar, the analogous time to do that would be when the object was allocated and initialized, but there's no support in Objective C to do that. You'd need to have a stronger definition of what an object constructor can do (more like Java's constructors than Objective C's initializers).
The closest you can get would be something like this:
#interface myClass () {
id * objArray;
}
-(instancetype)initWithRow:(size_t)row col:(size_t)col {
self = [super init];
if (self) {
objArray = calloc(row * col * sizeof(id));
}
return self;
}
-(void)dealloc {
free(objArray);
}
In that case, you're declaring the ivar as a pointer and managing the storage yourself (and the stride, for a multi-dimensional array).
Obviously, NSArray is better in all possible ways.

Override UIColor's isEqual: method within Category

I am trying to override the UIColor isEqual: method. I am doing so within a category method, however it does not seem to get called, either from NSArray's containsObject:, or even when called directly, as shown below.
It has been exposed as a method in the category's header file and I have also checked that the category has been imported to the implementation file I am working on.
Where it is being called directly:
UIColor *col = [UIColor eightBitColorWithRed:pxl.red green:pxl.green blue:pxl.blue];
int index = -1;
for (int i = 0; i < self.colorArrayM.count; i++) {
if ([col isEqual:((UIColor*)self.colorArrayM[i])]) {
index = i;
break;
}
}
And the category methods:
-(BOOL) isEqual:(id)otherColor {
if (otherColor == self)
return YES;
if (!otherColor || ![otherColor isKindOfClass:[self class]])
return NO;
return [self isEqualToColor:otherColor];
}
-(BOOL) isEqualToColor:(UIColor*)otherColor {
if (self == otherColor)
return YES;
unsigned char r0, r1, g0, g1, b0, b1;
[self eightBitRed:&r0 green:&g0 blue:&b0];
[otherColor eightBitRed:&r1 green:&g1 blue:&b1];
return r0 == r1 && g0 == g1 && b0 == b1;
}
The short answer is that categories are not intended to override existing methods:
Although the Objective-C language currently allows you to use a category to override methods the class inherits, or even methods declared in the class interface, you are strongly discouraged from doing so. A category is not a substitute for a subclass. There are several significant shortcomings to using a category to override methods:
When a category overrides an inherited
method, the method in the category
can, as usual, invoke the inherited
implementation via a message to super.
However, if a category overrides a
method that exists in the category's
class, there is no way to invoke the
original implementation.
A category cannot reliably override methods declared in another category of the same class.
This issue is of particular significance because many of the Cocoa classes are implemented using categories. A framework-defined method you try to override may itself have been implemented in a category, and so which implementation takes precedence is not defined.
The very presence of some category methods may cause behavior changes across all frameworks. For example, if you override the windowWillClose: delegate method in a category on NSObject, all window delegates in your program then respond using the category method; the behavior of all your instances of NSWindow may change. Categories you add on a framework class may cause mysterious changes in behavior and lead to crashes.
You will need to swizzle the original isEqual: method to use your own implementation. There is a great NSHipster article on swizzling that should get you started.

Debugging trivial functions in Objective-C

Recently I just started working on iOS game programming, and I find several things confusing. (FYI, I am working on a simple game with code provided on makegamewith.us)
First, I just found out that only the main function is executed. By this I mean we use the main function to activate iOS simulator, so that we will be able to load our game. Then I realize that breakpoints only work in main functions. As I put breakpoints in other files (such as creature.m, a game component), despite that I use a function to create creature objects in the game, Xcode won't stop at that function. The iOS simulator will be called, and then the game will be automatically loaded.
So here is the question: how can I debug then?
I assume that function is called when I run the game, but Xcode just ignores any other function in other files except the main function in main.m.
Also, I encountered several "Couldn't find member variable" situations. I wonder how to prevent this from happening. The whole sprite builder publishing to Xcode thing appears blurry. I would appreciate if someone can explain how the whole thing works.
Update:
I realize that I didn't explicitly call any of the functions I have in other files (for instance, Grid.m as shown below). By main function, I mean the int main function in main.m. So the problem might possibly be that I didn't explicitly call that function in main? (but I think what main.m is responsible for is launching the program.)
In main.m:
int main(int argc, char *argv[]) {
#autoreleasepool //if I put a breakpoint here this will definitely work
{
int retVal = UIApplicationMain(argc, argv, nil, #"AppController");
return retVal;
}
}
Grid.m
#import "Grid.h"
#import "Creature.h"
// these are variables that cannot be changed
static const int GRID_ROWS = 8;
static const int GRID_COLUMNS = 10;
#implementation Grid {
NSMutableArray *_gridArray;
float _cellWidth;
float _cellHeight;
}
- (void)onEnter
{
[super onEnter];
[self setupGrid];
// accept touches on the grid
self.userInteractionEnabled = YES;
}
- (void)setupGrid //****if I put breakpoint here, it doesn't work****
{
// divide the grid's size by the number of columns/rows to figure out the right width and height of each cell
_cellWidth = self.contentSize.width / GRID_COLUMNS;
_cellHeight = self.contentSize.height / GRID_ROWS;
float x = 0;
float y = 0;
// initialize the array as a blank NSMutableArray
_gridArray = [NSMutableArray array];
// initialize Creatures
for (int i = 0; i < GRID_ROWS; i++) {
// this is how you create two dimensional arrays in Objective-C. You put arrays into arrays.
_gridArray[i] = [NSMutableArray array];
x = 0;
for (int j = 0; j < GRID_COLUMNS; j++) {
Creature *creature = [[Creature alloc] initCreature];
creature.anchorPoint = ccp(0, 0);
creature.position = ccp(x, y);
[self addChild:creature];
// this is shorthand to access an array inside an array
_gridArray[i][j] = creature;
// make creatures visible to test this method, remove this once we know we have filled the grid properly
creature.isAlive = YES;
x+=_cellWidth;
}
y += _cellHeight;
}
}
#end
Take a look at your main() function -- almost the only thing it does is to call UIApplicationMain(). This is true for any iOS application. So the real work is being done by UIApplicationMain(), and we should find out about that. Here's the description from the docs:
This function instantiates the application object from the principal
class and instantiates the delegate (if any) from the given class and
sets the delegate for the application. It also sets up the main event
loop, including the application’s run loop, and begins processing
events. If the application’s Info.plist file specifies a main nib file
to be loaded, by including the NSMainNibFile key and a valid nib file
name for the value, this function loads that nib file.
So if there's a problem with your app, it's likely related to the application delegate, your AppController class. Set a breakpoint in your -[AppController application:willFinishLaunchingWithOptions:] method -- that's what the application itself will call on its delegate when the app is ready to run. Your UIApplicationMain() call in main() looks OK, so the debugger should hit a breakpoint in your ...didFinishLaunching... method. Step through that method and make sure that you're setting up a window, setting a root view controller, etc. If you're not sure what needs to happen, try creating a new single-view project and looking at the code that's provided there.

Random implementation of methods in cocos2d-iphone?

I have some "void" methods in my project using cocos2d-iphone and would like make them random. Unfortunately, I'v found only little information about genareting random numbers. Any help is appreciated!
If I understand correctly you want to call a random method, right?
Get your method signatures in an array:
NSArray* methods = #[#"myMethod1", #"myMethod2", #"myMethod3"];
Pick a method name:
NSString* method = methods[arc4random()%method.count];
Call it:
[self performSelector:NSSelectorFromString(method)];
It would be also wise to check that self can respond to that selector first.
Try this
.h
-(int) randomGenarete;
.mm
-(int) randomGenarete
{
int random_number = arc4random() % 100; // return random 0 to 99
return random_number;
}

Resources