Reputation: 37
I am using NSData
to send and receive messages such as text, picture and voice. In order to differ the type of message, I append a header. When messages arrive, I assign the NSString
object with the header and use [header isEqualToString:@"txt"]
to determine different operations.
This is my method to handle the arrived message:
int msgarrvd (void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
void *payloadptr = message->payload;
int payLoadLen = message->payloadlen;
NSLog(@"%d", payLoadLen);
NSMutableArray *msgArray = [NSMutableArray array];
NSData *dataHeader = [NSData dataWithBytes:payloadptr length:3];
NSStringEncoding strEncode = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF8);
NSString *header = [[NSString alloc] initWithData:dataHeader encoding:strEncode];
NSLog(@"header:%@", header);
if ([header isEqualToString:@"txt"]) {
// text message
NSData *content = [NSData dataWithBytes:payloadptr length:payLoadLen];
NSString *msgContent = [[NSString alloc] initWithData:content encoding:strEncode];
NSString *subStr = [msgContent substringFromIndex:3];
[msgArray addObject:subStr];
}
if ([header isEqualToString:@"pic"]) {
// pic message
}
if ([header isEqualToString:@"voc"]) {
// voice message
NSMutableData *voiceData = [NSMutableData data];
[voiceData appendData:[NSData dataWithBytes:payloadptr length:payLoadLen]];
[voiceData replaceBytesInRange:NSMakeRange(3, payLoadLen) withBytes:payloadptr];
NSFileManager *fm = [NSFileManager defaultManager];
NSString *voicePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:@"sound.wav"];
[fm createFileAtPath:voicePath contents:voiceData attributes:nil];
NSURL *voiceURL = [NSURL fileURLWithPath:voicePath];
[msgArray addObject:voiceURL];
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionary];
if ([header isEqualToString:@"txt"]) {
[myDictionary setObject:header forKey:@"header"];
}
if ([header isEqualToString:@"pic"]) {
[myDictionary setObject:header forKey:
@"header"];
}
if ([header isEqualToString:@"voc"]) {
[myDictionary setObject:header forKey:@"header"];
}
[myDictionary setObject:msgArray forKey:@"content"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"MessageCome" object:nil userInfo:myDictionary];
MQTTClient_freeMessage(&message);
MQTTClient_free(payloadptr);
return 1;
}
The Xcode throw out-[__NSArrayM length]: unrecognized selector sent to instance 0x17dc7bb0
and Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM length]: unrecognized selector sent to instance 0x17dc7bb0'
What on earth the problem is?
Any help should be appreciate!
More
- (void)handle:(NSNotification *)notification
{
NSDictionary *message = [NSDictionary dictionaryWithDictionary:[notification userInfo]];
NSString *msgType = [message objectForKey:@"header"];
if ([msgType isEqualToString:@"txt"]) {
NSArray *array = [message objectForKey:@"content"];
NSString *msg = [[array valueForKey:@"description"] componentsJoinedByString:@""];
[self.messages addObject:msg];
NSLog(@"%@&%lu", msg, (unsigned long)[self.messages count]);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
if ([msgType isEqualToString:@"pic"]) {
// process pic
}
if ([msgType isEqualToString:@"voc"]) {
[self.messages addObject:[message objectForKey:@"content"]];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
Here is my observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle:) name:@"MessageCome" object:nil];
context void * NULL 0x00000000
topicName char * "/test" 0x16dcbe24
*topicName char '/' '/'
topicLen int 0 0
message MQTTClient_message * 0x16dcc064 0x16dcc064
struct_id char [4] ""
struct_version int 4 4
payloadlen int 28675 28675
payload void * 0x4409004 0x04409004
qos int 1 1
retained int 0 0
dup int 0 0
msgid int 25864 25864
payloadptr void * 0x4409004 0x04409004
payLoadLen int 28675 28675
msgArray __NSArrayM * @"1 object" 0x16dcc280
[0] NSURL * @"file:///var/mobile/Applications/417171BD-60C5-4DF6-989D-2983426B9CAD/Library/Documentationsound.wav" 0x16dd0760
dataHeader _NSInlineData * 3 bytes 0x16d48420
strEncode NSStringEncoding 4 4
header __NSCFString * @"voc" 0x16dca530
myDictionary __NSDictionaryM * 2 key/value pairs 0x16dd0830
[0] (null) @"content" : @"1 object"
[1] (null) @"header" : @"voc"
content NSData * nil
msgContent NSString * nil
subStr NSString * nil
voiceData NSMutableData * nil
fm NSFileManager * nil
voicePath NSString * nil
voiceURL NSURL * nil
This is my tableView dataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.messages count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.textLabel.text = [self.messages objectAtIndex:indexPath.row];
}
// Configure the cell...
cell.textLabel.text = [self.messages objectAtIndex:indexPath.row];
return cell;
}
Upvotes: 1
Views: 2313
Reputation: 86651
This line (in msgarrvd
)
[myDictionary setObject:msgArray forKey:@"content"];
sets the content key of a dictionary to an array (a mutable one in fact).
This line (in the notification handler)
[self.messages addObject:[message objectForKey:@"content"]]
takes the object for the content key (an array remember) and adds it to the messages
array.
This line (in tableView:cellForRowAtIndexPath:
)
cell.textLabel.text = [self.messages objectAtIndex:indexPath.row];
takes an object from the messages
array (which was itself an array, remember) and tries to assign it to a property called text
which is expected to be a string. Then somewhere inside the Cocoa library something wants to find out how long the string is, but it is not a string, it's an array and arrays don't respond to -length
.
Upvotes: 1
Reputation: 122391
Here is the error:
if ([msgType isEqualToString:@"voc"]) {
[self.messages addObject:[message objectForKey:@"content"]]; // HERE
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
As you've already shown that @"content"
is an array:
myDictionary __NSDictionaryM * 2 key/value pairs 0x16dd0830
[0] (null) @"content" : @"1 object" // HERE
[1] (null) @"header" : @"voc"
And your table code expects it to be a string:
cell.textLabel.text = [self.messages objectAtIndex:indexPath.row];
Hence the unrecognised selector error, as the label's text is expected to be an NSString
object and not an NSArray
object.
Note the difference in how you populate your data source for @"voc"
and @"txt"
type messages.
Upvotes: 1
Reputation: 19802
To get size of NSMutableArray you need to call:
[array count]
not length
The error tells you are calling "length" on a NSMutableArray class - this is why it crashes. It's not in the code you pasted - so best put a exception breakpoint and check from which line it is coming.
Upvotes: 1