IT story

왜 ivar을 사용 하시겠습니까?

hot-time 2020. 6. 11. 08:31
반응형

왜 ivar을 사용 하시겠습니까?


나는 보통이 질문이 다른 방법으로 질문되는 것을 본다 . (그리고 나는이 Q에 대한 bbum의 대답을 좋아합니다).

내 코드에서 거의 독점적으로 속성을 사용합니다. 그러나 iOS를 오랫동안 개발해온 전통적인 계약자 인 계약자와 협력하고 있습니다. 그는 속성을 거의 선언하지 않고 ivar에 의존하는 코드를 작성합니다. 나는 그가 getter / setter를 거치지 않는 최소한의 성능 향상을 위해 Objective C 2.0 (Oct '07) 및 2)까지 속성이 항상 존재하지 않았기 때문에 그가 사용했기 때문에 이것을 사용한다고 가정합니다.

그가 유출하지 않는 코드를 작성하는 동안 여전히 ivar보다 속성을 사용하는 것이 좋습니다. 우리는 그것에 대해 이야기했으며 KVO를 사용하지 않았고 메모리 문제를 처리 한 경험이 있기 때문에 속성을 사용해야 할 이유가 거의 없다고 생각합니다.

내 질문은 더 있습니다 ... 왜 경험이 많은 ivar 기간을 사용하고 싶습니까? ivar 사용이 정당화 될 수있는 성능 차이가 실제로 있습니까?

또한 명확히하기 위해 필요에 따라 세터와 게터를 재정의하고 게터 / 세터 내부의 해당 속성과 관련된 ivar을 사용합니다. 그러나 getter / setter 또는 init 외부에서는 항상 self.myProperty구문을 사용 합니다.


편집 1

좋은 답변을 보내 주셔서 감사합니다. 내가 잘못 생각한 것 중 하나는 ivar을 사용하면 속성이없는 곳에서 캡슐화를 얻는 것입니다. 클래스 연속에서 속성을 정의하십시오. 외부인으로부터 속성이 숨겨집니다. 인터페이스에서 속성을 readonly로 선언하고 구현에서 다음과 같이 readwrite로 재정의 할 수도 있습니다.

// readonly for outsiders
@property (nonatomic, copy, readonly) NSString * name;

수업이 계속 진행됩니다.

// readwrite within this file
@property (nonatomic, copy) NSString * name;

그것을 완전히 "비공개"시키려면 클래스 연속에서만 선언하십시오.


캡슐화

ivar이 개인용 인 경우 프로그램의 다른 부분은 쉽게 접근 할 수 없습니다. 선언 된 속성을 사용하면 영리한 사람들이 접근자를 통해 쉽게 액세스하고 변경할 수 있습니다.

공연

예, 일부 경우에 차이가있을 수 있습니다. 일부 프로그램은 프로그램의 특정 부분에서 실시간 메시징을 사용할 수없는 제약 조건이 있습니다. 다른 경우에는 속도를 위해 직접 액세스 할 수 있습니다. 다른 경우에는 objc 메시징이 최적화 방화벽 역할을하기 때문입니다. 마지막으로 참조 카운트 작업을 줄이고 최대 메모리 사용을 최소화 할 수 있습니다 (올바르게 수행 된 경우).

사소한 유형

예 : C ++ 유형을 사용하는 경우 직접 액세스가 더 나은 방법 일 수 있습니다. 복사 할 수 없거나 복사하기가 쉽지 않을 수 있습니다.

멀티 스레딩

많은 ivar들이 서로 의존적입니다. 다중 스레드 컨텍스트에서 데이터 무결성을 보장해야합니다. 따라서 중요한 섹션에서 여러 멤버에 직접 액세스하는 것이 좋습니다. 상호 의존적 데이터에 대한 접근자를 사용하는 경우 일반적으로 잠금은 재진입해야하며 더 많은 획득 (종종 더 많은 시간이 소요됨)이 종종 발생합니다.

프로그램 정확성

서브 클래스는 모든 메소드를 대체 할 수 있으므로 결국 인터페이스에 쓰는 것과 상태를 적절히 관리하는 것 사이에는 의미 상 차이가있을 수 있습니다. 프로그램 정확성에 대한 직접 액세스는 부분적으로 구성된 상태에서 특히 일반적입니다. 이니셜 라이저 및에서 dealloc직접 액세스를 사용하는 것이 가장 좋습니다. 또한 접근, 편의 생성자의 구현이 일반을 찾을 수 있습니다 copy, mutableCopy및 보관 / 직렬화 구현.

공개 readwrite 접근 자 사고 방식 이있는 모든 것에서 구현 세부 사항 / 데이터를 잘 숨기고있는 것으로 이동함에 따라 더 자주 발생 합니다. 때로는 서브 클래스의 재정의가 올바른 작업을 수행하기 위해 발생할 수있는 부작용을 올바르게 처리해야합니다.

이진 크기

기본적으로 모든 읽기 / 쓰기를 선언하면 프로그램 실행을 고려할 때 일반적으로 필요하지 않은 많은 접근 자 메서드가 생성됩니다. 따라서 프로그램에 약간의 지방과 로딩 시간이 추가됩니다.

복잡성을 최소화

어떤 경우에는 한 가지 방법으로 작성되고 다른 방법으로 읽히는 개인 bool과 같은 간단한 변수에 대해 추가 스캐 폴딩을 추가 + 유형 + 유지하는 것이 완전히 필요하지 않습니다.


속성이나 접근자를 사용하는 것이 나쁘다는 것은 아닙니다. 각각 중요한 이점과 제한 사항이 있습니다. 많은 OO 언어 및 디자인 접근 방식과 마찬가지로 ObjC에서 적절한 가시성을 가진 접근자를 선호해야합니다. 이탈해야 할 때가 있습니다. 따라서 ivar를 선언하는 구현에 대한 직접 액세스를 제한하는 것이 가장 좋습니다 (예 : 선언 @private).


다시 편집 1 :

우리 대부분은 숨겨진 이름을 알고있는 한 숨겨진 접근자를 동적으로 호출하는 방법을 기억했습니다. 한편, 우리 대부분은 한 하지 기억하는 방법 (KVC 이상) 볼 수 없습니다 제대로 액세스 인스턴스 변수에. 클래스 연속이 도움 이되지만 취약점이 생깁니다.

이 해결 방법은 분명합니다.

if ([obj respondsToSelector:(@selector(setName:)])
  [(id)obj setName:@"Al Paca"];

이제 KVC없이 ivar로만 사용해보십시오.


나를 위해 그것은 일반적으로 성능입니다. 객체의 ivar에 액세스하는 것은 그러한 구조체를 포함하는 메모리에 대한 포인터를 사용하여 C의 구조체 멤버에 액세스하는 것만 큼 빠릅니다. 실제로 Objective-C 객체는 기본적으로 동적으로 할당 된 메모리에있는 C 구조체입니다. 이는 일반적으로 코드가 얻을 수있는 속도만큼 빠르며 수동으로 최적화 된 어셈블리 코드도 그보다 빠를 수는 없습니다.

게터 / 설정을 통해 ivar에 액세스하려면 Objective-C 메서드 호출이 필요합니다. Objective-C 메서드 호출은 "일반"C 함수 호출보다 훨씬 느리며 (최소 3-4 배), 심지어 일반 C 함수 호출도 이미 여러 배 느립니다. 구조체 멤버에 액세스 재산의 특성에 따라, 컴파일러에 의해 생성 된 세터 / 게터 구현이 기능에 또 다른 C 함수 호출을 포함 할 수있다 objc_getProperty/ objc_setProperty이는 것으로 retain/ copy/ autorelease와 같은 객체 필요하고 필요한 경우 추가 원자 속성에 대한 spinlocking 수행합니다. 이것은 쉽게 비싸 질 수 있고 나는 50 % 느리다는 것에 대해 이야기하고 있지 않다.

이것을 시도하자 :

CFAbsoluteTime cft;
unsigned const kRuns = 1000 * 1000 * 1000;

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    testIVar = i;
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"1: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
    [self setTestIVar:i];
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"2: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);

산출:

1: 23.0 picoseconds/run
2: 98.4 picoseconds/run

이것은 4.28 배 더 느리며 이것은 원자가 아닌 원시적 int이며, 가장 좋은 경우입니다 . 대부분의 다른 경우는 더 나쁩니다 (원자 NSString *속성을 사용해보십시오 ). 따라서 각 ivar 액세스가 예상보다 4-5 배 느리다는 사실을 알 수 있다면 속성을 사용하는 것이 좋지만 (적어도 성능에 관해서는) 성능이 저하되는 상황이 많이 있습니다. 완전히 받아 들일 수 없습니다.

2015-10-20 업데이트

어떤 사람들은 이것이 실제 문제가 아니라고 주장합니다. 위의 코드는 순전히 합성이며 실제 응용 프로그램에서는 결코 눈치 채지 못할 것입니다. 이제 실제 샘플을 사용해 봅시다.

아래 코드는 Account객체를 정의 합니다. 계정에는 소유자의 이름 ( NSString *), 성별 ( enum) 및 연령 ( unsigned)과 잔액 ( int64_t) 을 설명하는 속성이 있습니다 . 계정 개체에는 init메서드와 compare:메서드가 있습니다. compare:방법은 다음과 같이 정의됩니다. 남성 이전의 여성 주문, 알파벳순으로 된 이름, 오래된 이전의 젊은 주문, 낮은 주문부터 높은 주문까지

사실이 두 계정 클래스가 존재 AccountA하고 AccountB. 구현을 살펴보면 compare:메소드 가 하나만 제외하고 거의 완전히 동일하다는 것을 알 수 있습니다. AccountA개체에 액세스 자신의 특성을 하면서, 방법 (게터)에 의해 AccountB개체에 액세스 자신의 특성을 바르에 의해. 그것은 정말로 유일한 차이점입니다! 그들은 둘 다 getter로 비교하기 위해 다른 객체의 속성에 액세스합니다 (ivar로 액세스하는 것은 안전하지 않습니다! 다른 객체가 하위 클래스이고 getter를 재정의하면 어떻게됩니까?). 또한 ivar로 자신의 속성에 액세스해도 캡슐화가 중단되지 않습니다 (ivar은 여전히 ​​공개되지 않음).

테스트 설정은 정말 간단합니다. 1 개의 Mio 임의 계정을 생성하고 배열에 추가하고 해당 배열을 정렬하십시오. 그게 다야. 물론 두 개의 배열이 있습니다. 하나는 AccountA객체 용이고 다른 하나는 AccountB객체 용이며 두 어레이 모두 동일한 계정 (동일한 데이터 소스)으로 채워져 있습니다. 배열을 정렬하는 데 시간이 얼마나 걸리나요?

어제 내가 한 몇 가지 실행 결과는 다음과 같습니다.

runTime 1: 4.827070, 5.002070, 5.014527, 5.019014, 5.123039
runTime 2: 3.835088, 3.804666, 3.792654, 3.796857, 3.871076

보다시피, AccountB객체 배열을 정렬하는 것이 객체 배열을 정렬하는 보다 항상 훨씬 빠릅니다AccountA .

최대 1.32 초의 런타임 차이로 인해 차이가 없다고 주장하는 사람은 UI 프로그래밍을 수행하지 않는 것이 좋습니다. 예를 들어 큰 테이블의 정렬 순서를 변경하려는 경우 이러한 시간 차이로 인해 사용자에게 큰 차이가 생길 수 있습니다 (허용되는 UI와 느린 UI의 차이).

또한이 경우 샘플 코드는 여기서 실제로 수행되는 유일한 작업이지만 코드는 복잡한 시계의 작은 장비입니까? 모든 기어가 이와 같이 전체 프로세스를 느리게하면 결국 전체 시계 속도에 어떤 의미가 있습니까? 특히 하나의 작업 단계가 다른 작업 단계의 결과에 의존하는 경우 모든 비 효율성이 요약됩니다. 대부분의 비 효율성 자체는 문제가 아니며 전체 프로세스에 문제가되는 것은 그 자체입니다. 그리고 그러한 문제는 프로파일 러가 중요한 핫스팟을 찾는 것이기 때문에 프로파일 러가 쉽게 보여줄 수있는 것은 아니지만 이러한 비 효율성 자체는 핫스팟이 아닙니다. CPU 시간은 평균적으로 그 사이에 분산되어 있지만 각 CPU에는 그 비율이 매우 적으므로 최적화하는 데 총 시간 낭비가 있습니다. 그리고 사실입니다

CPU 시간에 대해 생각하지 않더라도 CPU 시간 낭비가 완전히 허용된다고 생각하기 때문에 "무료"라고 가정하면 전력 소비로 인한 서버 호스팅 비용은 어떻습니까? 모바일 장치의 배터리 런타임은 어떻습니까? 동일한 모바일 앱을 두 번 작성하는 경우 (예 : 자체 모바일 웹 브라우저), 한 번만 모든 클래스가 getter 만 자신의 속성에 액세스하는 버전이고 모든 클래스가 ivar 만 해당 속성에 액세스하는 버전 인 경우 첫 번째 앱을 계속 사용하면 배터리는 기능적으로 동일하지만 사용자에게는 두 번째 배터리를 사용하는 것보다 훨씬 빠릅니다.

다음은 main.m파일 코드입니다 (코드는 ARC를 사용하도록 설정하고 컴파일 할 때 최적화를 사용하여 전체 효과를 확인하십시오).

#import <Foundation/Foundation.h>

typedef NS_ENUM(int, Gender) {
    GenderMale,
    GenderFemale
};


@interface AccountA : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountA *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


@interface AccountB : NSObject
    @property (nonatomic) unsigned age;
    @property (nonatomic) Gender gender;
    @property (nonatomic) int64_t balance;
    @property (nonatomic,nonnull,copy) NSString * name;

    - (NSComparisonResult)compare:(nonnull AccountB *const)account;

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance;
@end


static
NSMutableArray * allAcocuntsA;

static
NSMutableArray * allAccountsB;

static
int64_t getRandom ( const uint64_t min, const uint64_t max ) {
    assert(min <= max);
    uint64_t rnd = arc4random(); // arc4random() returns a 32 bit value only
    rnd = (rnd << 32) | arc4random();
    rnd = rnd % ((max + 1) - min); // Trim it to range
    return (rnd + min); // Lift it up to min value
}

static
void createAccounts ( const NSUInteger ammount ) {
    NSArray *const maleNames = @[
        @"Noah", @"Liam", @"Mason", @"Jacob", @"William",
        @"Ethan", @"Michael", @"Alexander", @"James", @"Daniel"
    ];
    NSArray *const femaleNames = @[
        @"Emma", @"Olivia", @"Sophia", @"Isabella", @"Ava",
        @"Mia", @"Emily", @"Abigail", @"Madison", @"Charlotte"
    ];
    const NSUInteger nameCount = maleNames.count;
    assert(maleNames.count == femaleNames.count); // Better be safe than sorry

    allAcocuntsA = [NSMutableArray arrayWithCapacity:ammount];
    allAccountsB = [NSMutableArray arrayWithCapacity:ammount];

    for (uint64_t i = 0; i < ammount; i++) {
        const Gender g = (getRandom(0, 1) == 0 ? GenderMale : GenderFemale);
        const unsigned age = (unsigned)getRandom(18, 120);
        const int64_t balance = (int64_t)getRandom(0, 200000000) - 100000000;

        NSArray *const nameArray = (g == GenderMale ? maleNames : femaleNames);
        const NSUInteger nameIndex = (NSUInteger)getRandom(0, nameCount - 1);
        NSString *const name = nameArray[nameIndex];

        AccountA *const accountA = [[AccountA alloc]
            initWithName:name age:age gender:g balance:balance
        ];
        AccountB *const accountB = [[AccountB alloc]
            initWithName:name age:age gender:g balance:balance
        ];

        [allAcocuntsA addObject:accountA];
        [allAccountsB addObject:accountB];
    }
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        @autoreleasepool {
            NSUInteger ammount = 1000000; // 1 Million;
            if (argc > 1) {
                unsigned long long temp = 0;
                if (1 == sscanf(argv[1], "%llu", &temp)) {
                    // NSUIntegerMax may just be UINT32_MAX!
                    ammount = (NSUInteger)MIN(temp, NSUIntegerMax);
                }
            }
            createAccounts(ammount);
        }

        // Sort A and take time
        const CFAbsoluteTime startTime1 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAcocuntsA sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime1 = CFAbsoluteTimeGetCurrent() - startTime1;

        // Sort B and take time
        const CFAbsoluteTime startTime2 = CFAbsoluteTimeGetCurrent();
        @autoreleasepool {
            [allAccountsB sortedArrayUsingSelector:@selector(compare:)];
        }
        const CFAbsoluteTime runTime2 = CFAbsoluteTimeGetCurrent() - startTime2;

        NSLog(@"runTime 1: %f", runTime1);
        NSLog(@"runTime 2: %f", runTime2);
    }
    return 0;
}



@implementation AccountA
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (self.gender != account.gender) {
            if (self.gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![self.name isEqualToString:account.name]) {
            return [self.name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (self.age != account.age) {
            if (self.age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (self.balance != account.balance) {
            if (self.balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end


@implementation AccountB
    - (NSComparisonResult)compare:(nonnull AccountA *const)account {
        // Sort by gender first! Females prior to males.
        if (_gender != account.gender) {
            if (_gender == GenderFemale) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Otherwise sort by name
        if (![_name isEqualToString:account.name]) {
            return [_name compare:account.name];
        }

        // Otherwise sort by age, young to old
        if (_age != account.age) {
            if (_age < account.age) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // Last ressort, sort by balance, low to high
        if (_balance != account.balance) {
            if (_balance < account.balance) return NSOrderedAscending;
            return NSOrderedDescending;
        }

        // If we get here, the are really equal!
        return NSOrderedSame;
    }

    - (nonnull instancetype)initWithName:(nonnull NSString *const)name
        age:(const unsigned)age gender:(const Gender)gender
        balance:(const int64_t)balance
    {
        self = [super init];
        assert(self); // We promissed to never return nil!

        _age = age;
        _gender = gender;
        _balance = balance;
        _name = [name copy];

        return self;
    }
@end

가장 중요한 이유는 정보 숨기기 의 OOP 개념입니다 . 속성을 통해 모든 것을 노출하고 외부 객체가 다른 객체의 내부를 들여다 볼 수 있도록 허용하면 이러한 내부를 사용하므로 구현 변경이 복잡해집니다.

The "minimal performance" gain can quickly sum up and then become a problem. I know from experience; I work on an app that really takes the iDevices to their limits and we thus need to avoid unnecessary method calls (of course only where reasonably possible). To aid with this goal, we're also avoiding the dot syntax since it makes it hard to see the number of method calls on first sight: for example, how many method calls does the expression self.image.size.width trigger? By contrast, you can immediately tell with [[self image] size].width.

Also, with correct ivar naming, KVO is possible without properties (IIRC, I'm not an KVO expert).


Semantics

  • What @property can express that ivars can't: nonatomic and copy.
  • What ivars can express that @property can't:
    • @protected: public on subclasses, private outside.
    • @package: public on frameworks on 64 bits, private outside. Same as @public on 32 bits. See Apple's 64-bit Class and Instance Variable Access Control.
    • Qualifiers. For example, arrays of strong object references: id __strong *_objs.

Performance

Short story: ivars are faster, but it doesn't matter for most uses. nonatomic properties don't use locks, but direct ivar is faster because it skips the accessors call. For details read the following email from lists.apple.com.

Subject: Re: when do you use properties vs. ivars?
From: John McCall <email@hidden>
Date: Sun, 17 Mar 2013 15:10:46 -0700

Properties affect performance in a lot of ways:

  1. As already discussed, sending a message to do a load/store is slower than just doing the load/store inline.

  2. Sending a message to do a load/store is also quite a bit more code that needs to be kept in i-cache: even if the getter/setter added zero extra instructions beyond just the load/store, there'd be a solid half-dozen extra instructions in the caller to set up the message send and handle the result.

  3. Sending a message forces an entry for that selector to be kept in the method cache, and that memory generally sticks around in d-cache. This increases launch time, increases the static memory usage of your app, and makes context switches more painful. Since the method cache is specific to the dynamic class for an object, this problem increases the more you use KVO on it.

  4. Sending a message forces all values in the function to be spilled to the stack (or kept in callee-save registers, which just means spilling at a different time).

  5. Sending a message can have arbitrary side-effects and therefore

    • forces the compiler to reset all of its assumptions about non-local memory
    • cannot be hoisted, sunk, re-ordered, coalesced, or eliminated.

  6. In ARC, the result of a message send will always get retained, either by the callee or the caller, even for +0 returns: even if the method doesn't retain/autorelease its result, the caller doesn't know that and has to try to take action to prevent the result from getting autoreleased. This can never be eliminated because message sends are not statically analyzable.

  7. In ARC, because a setter method generally takes its argument at +0, there is no way to "transfer" a retain of that object (which, as discussed above, ARC usually has) into the ivar, so the value generally has to get retain/released twice.

None of this means that they're always bad, of course — there are a lot of good reasons to use properties. Just keep in mind that, like many other language features, they're not free.


John.


Properties vs. instance variables is a trade-off, in the end the choice comes down to the application.

Encapsulation/Information Hiding This is a Good Thing (TM) from a design perspective, narrow interfaces and minimal linkage is what makes software maintainable and understandable. It is pretty hard in Obj-C to hide anything, but instance variables declared in the implementation come as close as you'll get.

Performance While "premature optimisation" is a Bad Thing (TM), writing badly performing code just because you can is at least as bad. Its hard to argue against a method call being more expensive than a load or store, and in computational intensive code the cost soon adds up.

In a static language with properties, such as C#, calls to setters/getters can often be optimised away by the compiler. However Obj-C is dynamic and removing such calls is much harder.

Abstraction An argument against instance variables in Obj-C has traditionally been memory management. With MRC instance variables require calls to retain/release/autorelease to be spread throughout the code, properties (synthesized or not) keep the MRC code in one place - the principle of abstraction which is a Good Thing (TM). However with GC or ARC this argument goes away, so abstraction for memory management is no longer an argument against instance variables.


Properties expose your variables to other classes. If you just need a variable that is only relative to the class you're creating, use an instance variable. Here's a small example: the XML classes for parsing RSS and the like cycle through a bunch of delegate methods and such. It's practical to have an instance of NSMutableString to store the result of each different pass of the parse. There's no reason why an outside class would need to ever access or manipulate that string. So, you just declare it in the header or privately and access it throughout the class. Setting a property for it might only be useful to make sure there are no memory issues, using self.mutableString to invoke the getter/setters.


Backwards compatibility was a factor for me. I couldn't use any Objective-C 2.0 features because I was developing software and printer drivers that had to work on Mac OS X 10.3 as part of a requirement. I know your question seemed targeted around iOS, but I thought I'd still share my reasons for not using properties.

참고URL : https://stackoverflow.com/questions/9086736/why-would-you-use-an-ivar

반응형