26828


Objective-Cでは共通で扱う変数はAppDelegateで宣言するというのが一般的のようですが、  
共通変数と共通関数をまとめたクラスを作成し、  
他のクラスから値のset, get、メソッドの呼び出しを行うことはできないのでしょうか？

今までは下記のようにAppDelegateでHogeClassクラスを宣言、インスタンス化。  
そのインスタンスのアドレスを共有し他のクラス(VarClass)で使っていました。  
(ダメな事ですが何故共有できるのか仕組みを理解できていません・・・)

HogeClass1.h

[code]

    @interface HogeClass1 : NSObject
    
    @property (nonatomic, retain) NSString *mainTitle;
    @property (nonatomic, retain) NSString *mainTitleSub; 
    
    @end
    
[/code]

HogeClass1.m

[code]

    #import "HogeClass1.h"
    
    @implementation HogeClass1
    
    - (void)hogeCall {
        NSLog(@"in hogeCall");
    }
    
    @end
    
[/code]

AppDelegate.h

[code]

    #import "HogeClass1.h"
    #import "HogeClass2.h"
    #import "HogeClass3.h"
    
[/code]

AppDelegate.m

[code]

    #import "AppDelegate.h"
    
    @implementation AppDelegate
    
    HogeClass1 *hc1;
    HogeClass2 *hc2;
    
    HogeClass3 *hc3;
    HogeClass3 *hc3_sub;
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 共通クラスのインスタンス化
        hc1 = [[HogeClass1 alloc] init];
        hc2 = [[HogeClass2 alloc] init];
        hc3 = [[HogeClass3 alloc] init];
        hc3_sub = [[HogeClass3 alloc] init];
    
        return YES;
    }
    
    @end
    
[/code]

VarClass

[code]

    #import "VarClass.h"
    
    HogeClass1 *hc1;
    HogeClass2 *hc2;
    
    HogeClass3 *hc3;
    HogeClass3 *hc3_sub;
    
    @implementation VarClass
    
    - (void)viewDidLoad {
        // 変数の操作
        [hc1 setMainTitle:@"タイトル名"];  // このhc1はAppDelegateクラスでインスタンス化したhc1と同じアドレスを持つ
        [hc1 setMainTitleSub:[hc1 mainTitle]];
    
        [hc1 hogeCall];
    }
    
    @end
    
[/code]

しかしXcode 8 betaで動作確認を行っていたところ、

[code]

    duplicate symbol _変数名 in:
    　　　　　　　　　ファイルのパス
    　　　　　　　　　ファイルのパス
    ld: 400 duplicate symbol for architecture
    clang: error: linker command failed with exit code 1
    
[/code]

とエラーが発生し、今までの使い方ができなくなりました。

AppDelegateクラスでインスタンス化したhogeClass1を以下のように使うのでは？と思い試みたのですがインスタンス化されたものを取得できず・・・

[code]

    AppDelegate *appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    hogeClass1 *hoge1 = *[appdelegate hc1];
    
[/code]

共通クラスの扱い方に関して考えから違っているのでしょうか？


CC BY-SA 3.0
2016-06-16T11:11:56.960
26789
2016-06-18T06:30:08.080
2016-06-16T21:12:24.630
5519
14782
question
objective-c xcode
共通変数や関数をまとめたクラスを他のクラスから操作したい場合


あなたが掲載されたコードの`hc1`などはObjective-Cの機能を何かしら使っているのではなく、単にC言語のグローバル変数になっています。

Report navigatorでより詳細なエラーメッセージを表示させると、より根本的な原因に近づけます。

[code]

    duplicate symbol _hc1 in:
        /Users/dev/Library/Developer/Xcode/DerivedData/..../Build/Intermediates/MyGlobals.build/Debug-iphonesimulator/MyGlobals.build/Objects-normal/x86_64/AppDelegate.o
        /Users/dev/Library/Developer/Xcode/DerivedData/..../Build/Intermediates/MyGlobals.build/Debug-iphonesimulator/MyGlobals.build/Objects-normal/x86_64/VarClass.o
    ld: 1 duplicate symbol for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    
[/code]

AppDelegate.oとVarClass.oの両方に_hc1と言うシンボルが定義されているために起こっている問題です。

修正のためのざっくりした方針を書いてみると:

(1) 変数の実体宣言をどこかの.m(または.cなどヘッダ以外の)ファイルに置く  
(2) 変数の`extern`宣言をそのファイル用の.hファイル内に置く  
(3) 変数を使用するだけのファイルには変数宣言を置かず、(2)の.hファイルをimport/includeする

という形にします。あなたのコードにできるだけ当てはめてみると。

AppDelegate.h:

[code]

    #import <UIKit/UIKit.h>
    
    #import "HogeClass1.h"
    //...他のimport
    
    //変数の`extern`宣言をそのファイル(AppDelegate.m)用の.hファイル内に置く
    extern HogeClass1 *hc1;
    //extern HogeClass2 *hc2;
    //
    //extern HogeClass3 *hc3;
    //extern HogeClass3 *hc3_sub;
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    
    @property (strong, nonatomic) UIWindow *window;
    
    
    @end
    
[/code]

AppDelegate.m:

[code]

    #import "AppDelegate.h"
    
    //変数の実体宣言をどこかの.m(または.cなどヘッダ以外の)ファイルに置く
    HogeClass1 *hc1;
    //HogeClass2 *hc2;
    //
    //HogeClass3 *hc3;
    //HogeClass3 *hc3_sub;
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 共通クラスのインスタンス化
        hc1 = [[HogeClass1 alloc] init];
    //    hc2 = [[HogeClass2 alloc] init];
    //    hc3 = [[HogeClass3 alloc] init];
    //    hc3_sub = [[HogeClass3 alloc] init];
    
        return YES;
    }
    
    @end
    
[/code]

(変数の実体宣言を`@implementation`/`@end`の間に置いた時の挙動もバージョンによって異なるようです。グローバル変数は外側に置いた方が良いでしょう。

VarClass.m:

[code]

    #import "VarClass.h"
    
    //グローバルを使用する箇所では自分で宣言せず、宣言の入っている.hをimportする
    #import "AppDelegate.h"
    
    @implementation VarClass
    
    - (void)viewDidLoad {
        // 変数の操作
        [hc1 setMainTitle:@"タイトル名"];  // このhc1はAppDelegateクラスでインスタンス化したhc1と同じアドレスを持つ
        [hc1 setMainTitleSub:[hc1 mainTitle]];
    
        [hc1 hogeCall];
    }
    
    @end
    
[/code]

Objective-
Cコンパイラ(とリンカ)の挙動はC/C++ほどきちんと規格化されていないこともあり、使用するバージョンによって細かい挙動が違うのですが、上のような書き方をすれば、どのバージョンのコンパイラ/リンカでも確実に動くはずです。実際に自分のプロジェクトに適用した時にうまくいかない点があれば、コメント等でお知らせください。

(グローバル変数を乱用した場合の弊害についてはこのサイトを検索しても見つかると思います。これからどんどんグローバル変数が増えていくようなら、他の持ち方を考えた方が良いかも知れません。AppDelegateクラスのインスタンスプロパティにしてしまえば、ご質問の最後に書かれたようなやり方でアクセスできるようになります。)


CC BY-SA 3.0
2016-06-18T06:30:08.080
26828
2016-06-18T06:30:08.080
13972
26789
answer