amirrajan / rubymotion-applied

RubyMotion documentation provided by the community. Submit a pull request to the docs for a free one year indie subscription.
Apache License 2.0
49 stars 9 forks source link

Best practice subclassing iOS #102

Closed casper91 closed 6 years ago

casper91 commented 6 years ago

I was wondering what the "best practice" for subclassing would be.

Example:

class TileView < UIImageView
  attr_reader :letter, :side_length

  def init(letter, side_length)
    @letter = letter
    @side_length = side_length

    image = UIImage.imageNamed("tile.png")

    self.initWithImage(image)

    scale = side_length / image.size.width
    self.frame = CGRectMake(0, 0, image.size.width * scale, image.size.height * scale)

    self
  end
end

tile = TileView.alloc.init(letter, tile_side)

This alloc annoys me bit and isn't very Rubyish. This might be something I have to adapt to.

When using alloc do I also have to dealloc to free memory when not used anymore or is this happing automatic?

wndxlori commented 6 years ago

As long as you don’t leave any strong references around, it’ll get cleaned up

andrewhavens commented 6 years ago

@casper91 In this case, I think you should override the new class method:

class TileView < UIImageView
  attr_reader :letter, :side_length

  def self.new(letter, side_length)
    image = UIImage.imageNamed("tile.png")
    s = self.alloc.initWithImage(image)
    s.letter = letter
    s.side_length = side_length
    scale = side_length / image.size.width
    s.frame = CGRectMake(0, 0, image.size.width * scale, image.size.height * scale)
    s
  end
end

Then you can call TitleView.new(letter, tile_side)

hboon commented 6 years ago

Probably a better of style, but I would suggest not to override or implement any kind of #new(x), #new(x, y, etc) variations other than #new() (no args) with objects that are bridged from Cocoa (Touch). Do exactly the same thing as @andrew suggsted, but name the method something else, say:

  1. def self.new_with_letter(letter, side_length) or
  2. def self.create_instance(letter, side_length) or
  3. def self.new(letter, side_length: side_length)

The last one works only if it's not a single arg and you use the Obj-C keyword arg style. In this case, there's 2 args. So the general approach that should work all the time is (1) or (2).

This is because if you override #new(singleArg), calls to #new (no arg) will break (unless you want to use default args). And being able to call #new on most classes bridged from Cocoa (Touch) is quite nice :) You might not call it on your own subclass for now, but who knows, one day you might forget, or pass it to another class or colleague that calls #new for you.

casper91 commented 6 years ago

As long as you don’t leave any strong references around, it’ll get cleaned up

What does a strong reference look?

Is there some documentation around WeakRef.new, #alloc.init, #new and strong ref. Not sure i completely understand how it works with RM?

Looks good I will try combine the suggested approaches.