I have implemented the following code in a view controller that is defined to conform to UICollectionViewDelegate
and UICollectionViewDataSource
:
objc_property_t *properties = class_copyPropertyList(self, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
[properitiesList addObject:[NSString stringWithUTF8String:propName]];
}
}
When I do that, I see not only my custom properties, but also a few introduced by those protocols, namely: hash
, superclass
, description
and debugDescription
.
How do I show just the properties from my subclass, and not those introduced by those protocols?
The class_copyPropertyList
method will only return properties declared for the class, not those declared for superclass. As the header for class_copyPropertyList
says, it returns
An array of pointers of type
objc_property_t
describing the properties declared by the class. Any properties declared by superclasses are not included.
But the issue is that you've defined your class to conform to UICollectionViewDataSource
and UICollectionViewDelegate
. Thus properties introduced by those protocols are being interpreted as properties for your subclass, not for some superclass.
The simplest solution is to remove those protocol declarations and then the error goes away.
If you don't want to remove those protocol declarations (e.g. you want to still enjoy code completion for those protocol methods), you can create an abstract class that conforms to these protocols, and then subclass that. For example:
@interface AbstractViewControllerForCollectionView : UIViewController <UICollectionViewDelegate, UICollectionViewDataSource>
@end
@implementation AbstractViewControllerForCollectionView
// we have to implement the required methods; let's just warn the developer if they accidentally fail to subclass
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
NSAssert(FALSE, @"this must be subclassed");
return 0;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSAssert(FALSE, @"this must be subclassed");
return nil;
}
@end
And:
@interface ViewController : AbstractViewControllerForCollectionView
@property (nonatomic, strong) NSString *myPropertyName;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSArray *objects;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
NSLog(@"%@", [[self class] properties]);
}
+ (NSArray *)properties {
NSMutableArray *propertyList = [NSMutableArray array];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(self, &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if (propName) {
[propertyList addObject:[NSString stringWithUTF8String:propName]];
}
}
free(properties);
return propertyList;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.objects.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
// configure cell
return cell;
}
@end
The only downside here is that the compiler won't warn you if you fail to subclass the required methods. But at least you'll get a runtime failure/warning that describes the problem.