unrecognized selector with category NSMutableData - objective-c

I'm adding a category to NSData as follows:
// PacketCategories.h
#interface NSData(PacketSplit)
- (NSArray *)splitTransferredPackets:(NSData **)leftover;
#end
// PacketCategories.m
#implementation NSData(PacketSplit)
- (NSArray *)splitTransferredPackets:(NSData **)leftover {
NSMutableArray *ret = [NSMutableArray array];
const unsigned char *beginning = [self bytes];
const unsigned char *offset = [self bytes];
NSInteger bytesEnd = (NSInteger)offset + [self length];
while ((NSInteger)offset < bytesEnd) {
uint64_t dataSize[1];
NSInteger dataSizeStart = offset - beginning;
NSInteger dataStart = dataSizeStart + sizeof(uint64_t);
NSRange headerRange = NSMakeRange(dataSizeStart, sizeof(uint64_t));
[self getBytes:dataSize range:headerRange];
if (dataStart + dataSize[0] + (NSInteger)offset > bytesEnd) {
NSInteger lengthOfRemainingData = [self length] - dataSizeStart;
NSRange dataRange = NSMakeRange(dataSizeStart, lengthOfRemainingData);
*leftover = [self subdataWithRange:dataRange];
return ret;
}
NSRange dataRange = NSMakeRange(dataStart, dataSize[0]);
NSData *parsedData = [self subdataWithRange:dataRange];
[ret addObject:parsedData];
offset = offset + dataSize[0] + sizeof(uint64_t);
}
return ret;
}
#end
And then trying to call that category:
#import "PacketCategories.h"
NSMutableData *data = [NSMutableData data];
// Read some data
[data appendBytes:buffer length:bytesRead];
NSArray *dataPackets = [data splitTransferredPackets:&readLeftover];
Which gets the following error:
-[NSConcreteMutableData splitTransferredPackets:]: unrecognized selector sent to instance 0x6e6f7b0
[ERROR] The application has crashed with an unhandled exception. Stack trace:
Any ideas? Does NSConcreteMutableData not inherit from NSData?
Other suggested answers (Objective-C Category Causing unrecognized selector) have suggested that the file isn't linked in, which is not possible because other categories defined in this file are used just fine.
Thanks

Ookay. I had the same problem, but with a different outcome. Briefly, the problem was in my project file. The category files showed up in the project navigator - I can load/edit, etc. But the linker did not know to link with them. The way I found this was pulling on another thread - creating a dummy concrete class in my category files to force the linker to include them. No luck. Then I tried to instantiate an instance of the dummy class in my app. Eureka - I now get a linker error! So, I simply removed and re-added the category files to the project and now all is well. Not sure how the project file got out of whack ( svn merge? ), but there it is.

Delete the Category files and add them again checking the target.
Solved for me.

Related

How to parse and take only this string value

I wanted to get only array string value app. As example(SLGoogleAuth ,HalfTunes,TheBackgrounder,Calculiator) . But don't know how to do?
It's a code.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSLog(#"apps: %#", [workspace performSelector:selectorALL]);
}
It's output:
Thanks in advance
You do not want to parse that. NSLog prints out a description of an object. You want to access that value directly.
[LSApplicationWorkspace allApplications];
returns NSArray of LSApplicationProxy. LSApplicationProxy class has a ivar _bundleURL that contains information that you need. You need runtime functions to access it. Working example below:
// #import <objc/runtime.h>
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSArray* appProxies = [workspace performSelector:selectorALL];
Ivar bundleUrlIvar = class_getInstanceVariable([appProxies.firstObject class], "_bundleURL");
NSMutableString* result = [NSMutableString string];
for (id appProxy in appProxies)
{
NSURL* url = object_getIvar(appProxy, bundleUrlIvar);
// at this point you have the information and you can do whatever you want with it
// I will make it a list as you asked
if (url)
{
[result appendFormat:#",%#", [url lastPathComponent]];
}
}
if (result.length > 0)
{
// remove comma from beginning of the list
[result deleteCharactersInRange:NSMakeRange(0, 1)];
}
NSLog(#"apps: %#", result);
Note that this will be rejected by AppStore as you are using private apis. So use at your own discretion.

New in Objective C. Simple bug at runtime. Thread 1: breakpoint 1.1

I am learning Objective C and I have tried to override my superclass (NSObject) description method in my BNRItem class. Though it seems like I have done everything right, my NSLog does not seem to use my overridden description method. Instead I see a Thread 1 Breakpoint 1.1 in my definition of the description method and more precisely, where I defined the descriptionString.
Here is my console output.
2015-08-30 20:49:00.622 RandomItems[46034:1002101] Zero
2015-08-30 20:49:00.623 RandomItems[46034:1002101] One
2015-08-30 20:49:00.623 RandomItems[46034:1002101] Two
2015-08-30 20:49:00.623 RandomItems[46034:1002101] Three
(lldb)
My main.m file:
#import <Foundation/Foundation.h>
#import "BNRItem.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
// Create a mutable array object, store its address in items variable...
NSMutableArray *items = [[NSMutableArray alloc] init];
//Send the message addObject: to the NSMutableArray pointed
//by the variable item, passing a string each time
[items addObject:#"One"];
[items addObject: #"Two"];
[items addObject: #"Three"];
// Send another message, insertObject:atIndex;, to that same array object
[items insertObject:#"Zero" atIndex:0];
// For every item in the items array ...
for (NSString *item in items){
//Log the description of item
NSLog(#"%#", item);
}
// Create a BNRItem instance and log its instance variables in the console
BNRItem *item = [[BNRItem alloc] init];
// Set item name to Red Sofa
item.itemName = #"Red Sofa";
item.serialNumber= #"A1B2C";
item.valueInDollards = 100;
//
NSLog(#"%#", item);
//Destroy the mutable array object
items = nil ;
}
return 0;
}
My header file:
#import <Foundation/Foundation.h>
#interface BNRItem : NSObject
{
NSString *_itemName;
NSString *_serialNumber;
int _valueInDollards;
NSDate *_dateCreated;
}
- (void)setItemName:(NSString *)str;
- (NSString *)itemName;
- (void)setSerialNumber:(NSString *)str;
- (NSString *)serialNumber;
- (void)setValueInDollards:(int)v;
- (int)valueInDollards;
- (NSDate *)dateCreated;
#end
And finally, my implementation file:
#import "BNRItem.h"
#implementation BNRItem
- (void)setItemName:(NSString *)str
{
_itemName = str;
}
- (NSString *)itemName
{
return _itemName;
}
- (void)setSerialNumber:(NSString *)str
{
_serialNumber = str;
}
- (NSString *)serialNumber
{
return _serialNumber;
}
- ( void )setValueInDollards:(int)v
{
_valueInDollards = v;
}
- ( int )valueInDollards
{
return _valueInDollards;
}
-( NSDate * )dateCreated
{
return _dateCreated;
}
- ( NSString * )description
{
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%# (%#): Worth %d, recorded on %#", self.itemName, self.serialNumber, self.valueInDollards, self.dateCreated];
return descriptionString;
}
#end
It sounds like you have simply set a breakpoint on a line in your -description method. The debugger is stopping your program at the breakpoint. There's no indication of an actual error.
If you hit the continue button in Xcode, your program would probably proceed just fine.
You can disable the breakpoint, tell Xcode to ignore all breakpoints, or delete the breakpoint if you don't want to break there. The breakpoint will look like a blue arrow in the margins to the left of your code. Right-click or Control-click on it to see options.
To make Xcode ignore breakpoints, toggle the breakpoint button in the debugging toolbar. It also looks like a right-pointing arrow. It will be filled in blue if breakpoints are enabled. It will be an outline if they're disabled.

Assigning to 'NSUInteger *' (aka 'unsigned long *') from incompatible type 'const uint8_t *const' (aka 'const unsigned char *const')

I'm relatively new to programming in objective c so please don't kill me for this question.
Currently I'm working on a small program which should be able to make some changes to id3 tags of mp4 files. I've implanted the mp4v2 library and now I'm trying to write a method that reads out the tag informations of a specific file.
My problem is that I just cannot get the value of the tag "contentRating" into a NSUInteger var. The reference of the library says that I get a value like this: "const uint8_t *".
But how can I put this into a NSUInteger var?
Thank you very much for your help in advance!
Heres my code:
- (void)getSongInfo:(NSString *)path
title:(NSString *)t
album:(NSString *)alb
artist:(NSString *)art
rating:(NSUInteger *)r
albumart:(NSData *)aArt {
// Transform path in const char
const char *cPath = [path cStringUsingEncoding:NSASCIIStringEncoding];
// Load File
MP4FileHandle mp4File = MP4Modify(cPath);
// Create Tag var
const MP4Tags* tags = MP4TagsAlloc();
// Get Tags
MP4TagsFetch(tags, mp4File);
// Get Titel + Album + Interpret + Rating
t = [NSString stringWithUTF8String:tags->name];
alb = [NSString stringWithUTF8String:tags->album];
art = [NSString stringWithUTF8String:tags->artist];
r = tags->contentRating; <-- My Problem!
You could do something like this:
- (void)getSongInfo:(NSString *)path
title:(NSString **)t
album:(NSString **)alb
artist:(NSString **)art
rating:(NSUInteger *)r
albumart:(NSData **)aArt {
...
// Get Titel + Album + Interpret + Rating
*t = [NSString stringWithUTF8String:tags->name];
*alb = [NSString stringWithUTF8String:tags->album];
*art = [NSString stringWithUTF8String:tags->artist];
*r = (NSUInteger)(*tags->contentRating); <-- My Problem!
You would call this code like this:
NSString* title = nil;
NSString* album = nil;
NSString* artist = nil;
NSUInteger rating = 0;
NSData* albumArt = nil;
[self getSongInfo:path title:&title album:&album artist:&artist rating:&rating albumArt:&albumArt];
Respect to your code, my code contains two kind of changes:
arguments are passed by reference (so you can "pass back" a value to the caller);
*contentRating is being cast to the proper type (since NSUInteger is larger than uint8_t, this cast won't present any problem.
On the other hand, I would strongly suggest creating a class to hold the information about a song:
#interface MP3Info : NSObject
#property (nonatomic, retain) NSString* title;
...
#end
and the redeclare your method like:
- (MP3Info*)getSongInfo:(NSString *)path;
Hope it helps.

How can I get list of classes already loaded into memory in specific bundle (or binary)?

It's possible to get list of classes from a bundle via NSBundleDidLoadNotification. But I can't figure out how I can get them from already loaded bundle. (same bundle with code)
I'm trying to get class list of my application bundle. More specifically, the classes only in my application binary.
I looked at objc_getClassList, but it returns ALL classes and it's obviously too heavy for me. I need lightweight method. I found objc_copyClassNamesForImage by googling, but it's not documented, and I don't know how to use it safely. I think I can try to use it conventionally, but I want to find another more safe option before going there.
Another option would be to iterate through all the classes registered with the runtime and use +[NSBundle bundleForClass:] on each one to figure out which one it came from. You can then sort things into sets based on the result.
Something like this:
#interface NSBundle (DDAdditions)
- (NSArray *)definedClasses_dd;
#end
#implementation NSBundle (DDAdditions)
- (NSArray *)definedClasses_dd {
NSMutableArray *array = [NSMutableArray array];
int numberOfClasses = objc_getClassList(NULL, 0);
Class *classes = calloc(sizeof(Class), numberOfClasses);
numberOfClasses = objc_getClassList(classes, numberOfClasses);
for (int i = 0; i < numberOfClasses; ++i) {
Class c = classes[i];
if ([NSBundle bundleForClass:c] == self) {
[array addObject:c];
}
}
free(classes);
return array;
}
#end
Then you can call:
NSLog(#"%#", [[NSBundle mainBundle] definedClasses_dd]);
Try this magic:
- (NSArray *)getClassNames {
NSMutableArray *classNames = [NSMutableArray array];
unsigned int count = 0;
const char **classes = objc_copyClassNamesForImage([[[NSBundle mainBundle] executablePath] UTF8String], &count);
for(unsigned int i=0;i<count;i++){
NSString *className = [NSString stringWithUTF8String:classes[i]];
[classNames addObject:className];
}
return classNames;
}
I could find some example for the function objc_copyClassNamesForImage at here.
http://www.opensource.apple.com/source/objc4/objc4-493.9/test/weak.m
// class name list
const char *image = class_getImageName(objc_getClass("NotMissingRoot"));
testassert(image);
const char **names = objc_copyClassNamesForImage(image, NULL);
testassert(names);
testassert(classInNameList(names, "NotMissingRoot"));
testassert(classInNameList(names, "NotMissingSuper"));
if (weakMissing) {
testassert(! classInNameList(names, "MissingRoot"));
testassert(! classInNameList(names, "MissingSuper"));
} else {
testassert(classInNameList(names, "MissingRoot"));
testassert(classInNameList(names, "MissingSuper"));
}
free(names);
The source code is unofficial but from Apple. So I decided to use this code until I find any better way.

Get property name as a string

I need a way to pass a property and get the name assigned to it. Any suggestions?
#property (nonatomic, retain) MyObject *crazyObject;
NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return #"crazyObject"
You can try this:
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
NSLog(#"Names: %#", propertyNames);
It's as simple as this...expanding upon what Chuck already mentioned:
#ifndef STR_PROP
#define STR_PROP( prop ) NSStringFromSelector(#selector(prop))
#endif
You then use it like so:
NSString *strProp = STR_PROP(myProperty);
Background
Keep in mind that properties are really just, to quote Apple, "a syntactical shorthand for declaring a class’s accessor methods." In fact, by itself, the #property declaration doesn't even work. Your #synthesize statement translates the #property into the equivalent of two methods:
- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;
Which one is used depends on the context surrounding your self.crazyObject. (#synthesize also creates a matching instance variable if you didn't do it yourself.) The offshoot of all this is that you can't really translate to and from a property with one single method.
Proposed Solution
You can use what Apple already provides:
NSString *foo = NSStringFromSelector(#selector(myClassProperty));
Or do something custom:
Given that self.crazyObject really translates to either [self crazyObject] or [self setCrazyObject:foo] by the time your code is running, ou'll probably need two methods, like:
- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;
You might then want at least 2 companion methods such as:
- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;
Within these methods, you can use the Foundation functions NSStringFromSelector and NSSelectorFromString to convert back and forth between SEL and NSString. Use whatever string manipulations you like to convert back and forth between your setter string (setCrazyObject) and your property name (crazyObject).
A complete solution is hard to provide without knowing the exact use case, but hopefully this provides some more clues for anyone trying to accomplish something similar. There might even be some useful things made possible by combining this approach with Oscar's answer.
Here is a function that returns the name of an ivar, so basically it not only returns the properties but any ivar of the class. I haven't found a way to get the property directly so I used the ivar trick.
#import <objc/objc.h>
/// -----
- (NSString *)nameOfIvar:(id)ivarPtr
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
id pointer = object_getIvar(self, ivar);
if(pointer == ivarPtr)
{
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
break;
}
}
free(ivars);
}
return name;
}
After searching and debugging i find solution for me...
Added #import <objc/runtime.h>
Methods object_getIvar(id obj, Ivar ivar) send bad access and app crashes. i modify some code and it worked great:
+(NSString*)stringWithProperty:(id)property withClass:(id)controller
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([controller class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
if ([controller valueForKey:name] == property)
{
break;
}
}
free(ivars);
}
return name;
}
Modifying the solution, it works when your object is allocated already, otherwise it returns nil:-
NSString * NSStringFromProperty(NSObject* property, NSObject* class)
{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([class class], &propertyCount);
NSString *name = nil;
for (unsigned int i = 0; i < propertyCount; ++i)
{
name = [NSString stringWithUTF8String:property_getName(properties[i])];
NSObject *object = [class valueForKey:name];
if (object != nil && object == property)
{
break;
}
else
{
name = nil;
}
}
free(properties);
return name;
}
You can use
NSString *str = NSStringFromSelector(#selector(crazyObject));
The good thing about this approach is that:
Xcode will autocomplete word crazyObject for you.
When later on you will change the property name from crazyObject to myCrazyObject, Xcode will add a warning saying "unrecognized selector!" -- pretty good for debugging.
I use this method so often, that I even created a function, which allows to write less letters:
NSString * __nonnull sfs(SEL __nonnull theSelector)
{
if (!theSelector)
{
abort();
}
return NSStringFromSelector(theSelector);
}
Now your final solution can look like this:
NSString *str = sfs(#selector(crazyObject));
From Get property name as string, without using the runtime reference library, just define:
#define propertyKeyPath(property) (#""#property)
#define propertyKeyPathLastComponent(property) [[(#""#property) componentsSeparatedByString:#"."] lastObject]
And then you can do something like this:
NSLog(#"%#", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
You may check my approach at Gist to get the string for a property with autocompletion and compile-time check.
How to use:
Get the property name for a class:
#interface AnyClass : NSObject
#property (strong) NSData *data;
#end
// == My approach ==
// C string for a class
PropertyNameForClass(AnyClass, data); // ==> "data"
// NSString for a class
PropertyStringForClass(AnyClass, data); // ==> #"data"
// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = #"data";
Get the property name for a protocol:
#protocol AnyProtocol
#property (strong) NSDate *date;
#end
// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date); // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date); // ==> #"date"
Unconventional, hacky, ugly, late, but... as strong-named as it gets and works like a charm:
#define SOME_WAY_TO_GET_PROPERTY_NAME(p) p == p ? [[[[[[[NSString alloc] initWithCString:#p encoding:NSUTF8StringEncoding] componentsSeparatedByString:#"."] lastObject] componentsSeparatedByString:#" "] lastObject] stringByReplacingOccurrencesOfString:#"]" withString:#""] : #""
Sample usage:
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME(self.customer.surname)); // surname
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME([[self customer] birthDate])); // birthDate
...