【问题标题】:iOS access a single instance of an object from two different classesiOS 从两个不同的类访问一个对象的单个实例
【发布时间】:2012-07-26 20:19:51
【问题描述】:

是否可以在 A 类中创建一个对象的实例并从 B 类访问该对象的同一个实例?我正在尝试开发一个使用 NSInputStream 和 NSOutputStream 创建 TCP Socket 的应用程序,并且需要多个类才能访问它。

谢谢你, 特拉维斯·埃利奥特

编辑

这是我正在使用的代码。它是一个处理套接字连接的程序。我基本上需要能够从我的 appDelegate 和 View 控制器与同一个套接字进行通信。这是我根据您的帮助获得的代码。我使用 appDelegate 作为控件(在您的示例中为 D),也许我不能这样做。 CommunicationHub 是我需要从 AppDelegate 和 ViewController 控制相同实例的类。

AppDelegate.h

#import <UIKit/UIKit.h>
#import "ViewController.h"
#import "CommunicationHub.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate>{

ViewController *viewController;
CommunicationHub *cHub;
}

@property (strong, nonatomic) UIWindow *window;
@property (strong, retain) ViewController *viewController;
@property (strong, retain) CommunicationHub *cHub;

-(void)CreateInstances;


@end

AppDelegate.m

#import "AppDelegate.h"
#import "ViewController.h"

@implementation AppDelegate
@synthesize viewController;
@synthesize cHub;

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [self CreateInstances];
    // Override point for customization after application launch.
    return YES;
}

-(void)CreateInstances{
    NSLog(@"Inside CreateInstances");
    CommunicationHub *cHub = [[CommunicationHub alloc] init];
    viewController = [[ViewController alloc] init];
    [viewController initWithcHub:cHub];
    NSLog(@"ID of cHub in AppDelegate is %i", cHub);


}

- (void)applicationWillResignActive:(UIApplication *)application
{
    NSLog(@"Application Will Resign Active");
    [cHub disconnect];
}

@end

ViewController.h

#import <UIKit/UIKit.h> 
#import "CommunicationHub.h"

@interface ViewController : UIViewController 
{
    CommunicationHub *cHub;
}

@property (strong, nonatomic) IBOutlet UITextField *IPAddress;
@property (strong, nonatomic) IBOutlet UITextField *PortNumber;
- (IBAction)goAwayKeyBoard:(id)sender;
- (IBAction)touchBackground:(id)sender;
-(void) initWithcHub:(CommunicationHub *)ptr;
- (IBAction)connectSocket:(id)sender;
- (IBAction)disconnectSocket:(id)sender;

@end

ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
@synthesize IPAddress;
@synthesize PortNumber;

-(void) initWithcHub:(CommunicationHub *)ptr
{
    cHub = [[ptr retain]init];
    NSLog(@"id of cHub in ViewController is %i", cHub);
}

- (IBAction)connectSocket:(id)sender 
{
    //Called by button on UI.
    int portNumber = [PortNumber.text intValue];

    [cHub Connect:(int *)portNumber ipAddress:(IPAddress.text)];
}

- (IBAction)disconnectSocket:(id)sender 
{
    //Called by button on UI.
    [cHub disconnect];
}


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [self setIPAddress:nil];
    [self setPortNumber:nil];
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    } else {
        return YES;
    }
}

-(IBAction)goAwayKeyBoard:(id)sender{

    [self resignFirstResponder];
}

- (IBAction)touchBackground:(id)sender {
    [IPAddress resignFirstResponder];
    [PortNumber resignFirstResponder];
}


@end

CommunicationHub.h

#import <UIKit/UIKit.h>

NSInputStream *inputStream;
NSOutputStream *outputStream;

@interface CommunicationHub : NSObject <NSStreamDelegate>

- (void)Connect:(int *)port ipAddress:(NSString *)ipAddress;
- (void) disconnect;

@end

CommunicationHub.m

#import "CommunicationHub.h"

@implementation CommunicationHub

- (void)Connect:(int *)port ipAddress:(NSString *)ipAddress 
{
    NSLog(@"inside connect method");

    if ([inputStream streamStatus] == 0 ||[inputStream streamStatus] ==  5 ||[inputStream streamStatus] == 6 ||[inputStream streamStatus] == 7) 
    {
        NSString *myString = ipAddress;
        CFStringRef *myCFString = (__bridge CFStringRef)myString;
        CFReadStreamRef readStream;
        CFWriteStreamRef writeStream;
        CFStreamCreatePairWithSocketToHost(NULL, myCFString, port, &readStream, &writeStream);
        inputStream = (__bridge NSInputStream *)readStream;
        outputStream = (__bridge NSOutputStream *)writeStream;
        [inputStream setDelegate:self];
        [outputStream setDelegate:self];

        [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [inputStream open];
        [outputStream open];
    }
}

- (void) disconnect
{
    NSLog(@"inside disconnect method");

    if (inputStream != nil) {
        if ([inputStream streamStatus] == 2) {
            NSLog(@"Disconnecting Streams");
            [inputStream close];
            [outputStream close];
        }else {
            NSLog(@"Stream is not Open");
            int status = [inputStream streamStatus];
            NSLog(@"Stream Status is %i", status);
        }

    }else {
        NSLog(@"Input Stream equals Nil");
    }

}

@end

【问题讨论】:

  • 简而言之:将实例的指针传递给需要使用它的类。
  • 好的,我从哪里开始...首先:从“连接”中删除该 int * 强制转换。如果您不将其用作“int”参数,而是用作实际指针,这不仅是不必要的,而且还具有潜在的危险。下一篇:局部变量和实例变量不要使用同名,因为局部自然会隐藏实例变量,可能会导致泄漏。下一步:[[ViewController alloc] initWithCHub] -> 一个方法调用。不是初始化,然后再次“初始化”。 (或修改您的方法名称)。然而,抛开所有的设计缺陷……目前的问题到底是什么?
  • 我会纠正这些。我的主要问题是当应用程序启动时,我的 appDelegate 在本地调用 CreateInstances,它创建了 CommunicationHub 类和 ViewController 类的实例,然后它调用了 ViewController 上的方法 initWithcHub,我猜想这是为 CommunicationHub 创建一个名为 cHub 的对象。这可行,我可以在应用程序委托和视图控制器上打印 cHub 对象的 ID,但是当我按下 UI 上的按钮以调用 cHub 上的连接方法时,cHub 对象为零。似乎当我调用 initWithcHub 时,它是一个新实例。
  • 不,它不应该“创建对象”,而是“传递对已创建对象的引用”。请在保留后删除“init”,并告诉我它是否更好。
  • 我实际上确实尝试过删除 init,因为这似乎不正确,但似乎也不起作用。目前我的代码是 cHub = [ptr retain];我相信问题是当我在 initWithcHub:(CommunicationHub *)ptr 方法中调用这一行时,appDelegate 创建了 viewController 的新实例,因此 cHub 对象由该实例创建,但仅在该特定实例中可用,所以如果我按下按钮调用 viewController 中 cHub 对象为 nil 的方法。这可能吗?

标签: instance nsobject nsinputstream


【解决方案1】:

要让它变长: 假设你有 A、B 和 C 类。

C 需要可从 A 和 B 访问。 现在,假设我们有控制器 D,它实例化了 A 和 B。 让 D 先实例化 C,并将其存储在变量中。

C *C_Class = [[[C alloc] init] autorelease];

接下来,用 C 实例化 A 和 B(我在这里假设“保留属性”)

self.A_Class = [[[A alloc] initWithC:C_Class] autorelease];
self.B_Class = [[[B alloc] initWithC:C_Class] autorelease];

在这两个类中,你都需要实例方法:

- (id)initWithC:(C *)C_Class;

在 .m 文件中:

- (id)initWithC:(C *)C_Class {
    C_C = [C_Class retain]; //Assuming a regular instance variable
}

- (void) dealloc {
    [C_C release];  //All that is retained, must be released.
}

从这一刻起,您可以通过名称“C_C”访问 A 类和 B 类中 C 类的一个实例。

现在,请注意,这只是我脑后写的,容易出错,但应该没问题。

希望对你有帮助:)

编辑:由于OP似乎对这些代码片段有问题,我将添加更完整的代码。

让我们从控件类“D”开始。

D.h:

#import "A.h"
#import "B.h"

@interface D : NSObject

-(void)CreateInstances;

@property (strong, retain) A *A_Class;
@property (strong, retain) B *B_Class;

@end

d.m:

#import "D.h"
#import "C.h"

@implementation D

-(void)CreateInstances {
    C *C_Class = [[C alloc] init] autorelease];
    self.A_Class = [[[A alloc] initWithC:C_Class] autorelease];
    self.B_Class = [[[B alloc] initWithC:C_Class] autorelease];
}

@end

A.h(B.h 反映了这种行为,也许可以考虑为两者使用超类)

#import "C.h"

@interface A : NSObject {
    C *C_Class;           //Class Reference to the C-Object
}

-(id) initWithC:(C *)ptr; //Initialization method, which takes a 'C'-Object.

@end;

上午

#import "A.h"

@implementation A

- (id) initWithC:(C *)ptr {
    C_Class = [ptr retain]; //So the object doesn't get released prematurely.
}

- (void) dealloc {
    [C_Class release];      //To avoid memory leaks.
}

@end

现在,请记住,我是在脑海中写下这段代码,并没有通过编译器运行它,但它应该可以工作,避免拼写错误等。

另一个编辑: 在 OP 添加他自己的代码后,我将在此处发布相关的“更正”代码。

AppDelegate.m

-(void)CreateInstances{
    NSLog(@"Inside CreateInstances");
    cHub = [[CommunicationHub alloc] init]; //We're using the instance variable. Not the local one.
    viewController = [[ViewController alloc] initWithcHub:cHub];
    //[viewController initWithcHub:cHub];  //You may want to rethink your function names.
    NSLog(@"ID of cHub in AppDelegate is %i", cHub);
}

ViewController.m

//If a method names STARTS with 'init', it SHOULD initialize the object.
-(id) initWithcHub:(CommunicationHub *)ptr
{
    self = [super init];     //Calls the 'init' from the parent class.
    if(self) {
        cHub = [ptr retain]; //Just retain. After all, the object IS initialized already.
        NSLog(@"id of cHub in ViewController is %i", cHub);
    }
    return self;
}

- (void) dealloc {
    [cHub release]; //Basic rule of memory management: ALL that is retained, must be released. In dealloc, at latest.
}

- (IBAction)connectSocket:(id)sender 
{
    //Called by button on UI.
    //Do not pass a pointer to anything, unless you mean to use a pointer.
    [cHub Connect:[PortNumber.text intValue] ipAddress:(IPAddress.text)];
}

其余的应该是正确的,或者至少不是这个问题的主题。

【讨论】:

  • 哇,谈了很多要吸收的东西。我会试试这个,然后告诉你进展如何。
  • 那么你在A类和B类initWithC中创建的方法,C类需要自己传这个方法吗?我可以通过从 C 类传递它自己来调用此方法,并打印 id 并且它们匹配,但我无法从 A 或 B 类调用我的 C 类的任何方法。
  • 不,看来你误会了。在这个小组合中有第四节课。可以这么说,“主控制器”(在我的示例中为 D)。此主控制器创建 A、B 和 C 的实例(引用所有三个)。此外,请务必将头文件中的 C.h 导入 A 和 B 并创建指向类本身的指针,而不是“id”。如果您发布问题中的代码,我可能会更好地帮助您。评论不太适合这个。
  • 这个调用创建了 A 或 B 类的新实例,因此调用 C 对象为零:self.A_Class = [[[A alloc] initWithC:C_Class] autorelease];似乎是这里的确凿证据。
猜你喜欢
  • 2019-09-29
  • 1970-01-01
  • 2011-05-29
  • 2013-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多